我是Typescript的新手。
我想提取一个名为Mapper的类。
该映射器采用两个参数:
1.我们要映射的对象数据
2.定义地图后数据形状的地图
它具有一些方法,这些方法将根据我们要执行的映射类型返回MappedData。
class Mapper {
constructor(data: Data, dataMap: Map) {
}
mapKey(): MappedKeyData {
// return a mappedKeyData
}
}
// Usage
const source = {
Name: "Dudi"
}
const map = new Map([
['name', 'Name']
])
const mapper = new Mapper(source, map)
console.log(mapper.mapKey()) // {name: 'Dudi'}
问题是我想确保MappedKeyData的类型是一个接口,该接口具有在键映射之后定义的所有键。 (根据地图中定义的键)
例如:
interface MappedKeyData {
name: 'string'
}
这是使用Typescript的正确方法吗?如果是,该如何实施?谢谢...
答案 0 :(得分:1)
如果您确实想使用类和Map
对象,则可以,但是使用函数和普通的旧javascript对象会更简单。
我将展示一种执行此操作的方法,并且您可以根据需要进行调整(但请注意,TypeScript中的Map
并不是您所希望的类型,因此请避免使用它。)
这是mapKeys()
函数:
function mapKeys<D extends object, M extends Record<keyof M, keyof D>>(
data: D,
dataMap: M
) {
const ret = {} as { [K in keyof M]: D[M[K]] };
(Object.keys(dataMap) as Array<keyof M>).forEach(
<K extends keyof M>(k: K) => {
ret[k] = data[dataMap[k]];
}
);
return ret;
}
这是它的基本用法:
const mapped = mapKeys(
{
Name: "Dudi",
Age: 123
},
{ name: "Name", age: "Age" }
);
// const mapped: {
// name: string;
// age: number;
// }
console.log(mapped); // {name: 'Dudi', age: 123}
mapKeys()
在运行时的工作方式只是循环访问k
的键dataMap
并将返回对象中的类似属性设置为来自data
的数据按键dataMap[k]
。这就是ret[k] = data[dataMap[k]]
行。
其余大部分是类型注释和断言,以帮助编译器理解和验证我们在做什么。假设data
的类型为D
,而dataMap
的类型为M
,则返回类型将为mapped type {[K in keyof M]: D[M[K]] }
。这就是说它将具有与M
相同的键,并且每个键K
的值都将是D[M[K]]
类型(即我们look up { K
的1}}属性,然后查找M
的 that 属性。
因此,这应该适合您,具体取决于您的用例。
当然有很多警告。在某些情况下,我试图问您想看到什么,我可以肯定的是,如果不是{{的键,则您不希望在D
中输入一个值1}}。 constraint dataMap
应该保证。但是还有其他可能的边缘情况:
data
首先,必须相当强地键入M extends Record<keyof M, keyof D>
参数才能使其起作用。如果您只是这样做:
dataMap
然后,dataMap
最终将被推断为类型const source = { Name: "Didu", Age: 321 };
const map = { name: "Name", age: "Age" };
,而不是map
。而且您会得到一个错误:
{name: string, age: string}
解决此问题的方法是确保{name: "Name", age: "Age"}
具有string literal属性值,例如使用const
assertion时得到的值:
const badInfer = mapKeys(source, map); // error!
// ┌───────────────────────────> ~~~
// └─ Argument of type '{ name: string; age: string; }' is not assignable
// to parameter of type 'Record<"name" | "age", "Name" | "Age">'.
请注意,如果我们使用dataMap
代替对象,则default TypeScript type declarations for it不够坚固。 const fixedMap = { name: "Name", age: "Age" } as const; //
// inferred to be {readonly name: "Name"; readonly age: "Age"; }
const fixedInfer = mapKeys(source, fixedMap); // okay, right type now
意味着Map
中的任何键都可能映射到Map<K, V>
中的任何值。因此,K
充其量只能与类型V
等效,并且您的输出类型中会有不幸的new Map([["name","Name"],["age","Age"]])
。
没有什么可以阻止您将{name: "Name" | "Age", age: "Name" | "Age"}
中的某些键留在string | number
中:
data
没有什么能阻止您在dataMap
中多次映射const fewerProps = mapKeys({ Name: "Dodi", Age: 132 }, { name: "Name" });
// const fewerProps: { name: string }; // no age
console.log(fewerProps); // {name: "Dodi"}
中的相同键:
data
如果您的dataMap
对象具有index signature,则类型系统的行为会很奇怪,因为它将假定const duplicateProps = mapKeys(
{ Name: "Dido", Age: 231 },
{
name: "Name",
alsoName: "Name",
age: "Age",
alsoAge: "Age"
}
);
// const duplicateProps: {name: string; alsoName: string; age: number; alsoAge: number;}
console.log(duplicateProps); // {name: "Dido", alsoName: "Dido", age: 231, alsoAge: 231}
具有与索引匹配的任何可能的键:
data
这不是一个完美的结果,因为类型系统说data
具有类型const dictionary: { [k: string]: string | number } = { Name: "Didi", Age: 312 };
const confused = mapKeys(dictionary, {
dogs: "Dogs",
cats: "Cats",
age: "Age"
});
// const confused: { dogs: string | number; cats: string | number; age: string | number;}
console.log(confused); // {dogs: undefined, cats: undefined, age: 312 }
// note that "undefined" can sneak into index signatures
的{{1}}属性,但实际上在运行时是confused
。这是索引签名的陷阱之一,我们的映射器也陷入其中。
如果您决定要查看的内容并放入更复杂的类型断言和注释,则可以解决其中一些警告,但是我想我会在这里停留,并给您补充任何补丁。希望这足以给您一些指导。祝你好运!