我有一个函数,它获取一张地图并生成地图的双向投影:
export function keyTextBijection(map) {
const bijection = {};
Object.keys(map).forEach(key => {
bijection[key] = map[key];
bijection[map[key]] = key;
});
return bijection;
}如何在TypeScript中优雅地对函数进行签名?
发布于 2020-10-30 12:26:17
它有助于编写一个类型函数,将像{a: "A", b: "B"}这样的类型转换为与之相反的类型{A: "a", B: "b"}。TypeScript 4.1将通过microsoft/TypeScript#40336实现的映射类型as子句简化这一过程
// TS4.1+
type Invert<T extends Record<keyof T, PropertyKey>> = { [K in keyof T as T[K]]: K };但对于TypeScript 4.0及更低版本,您可以通过使用映射的条件类型在大多数情况下获得合理的等价物:
// TS4.0-
type Invert<T extends Record<keyof T, PropertyKey>> =
{ [K in T[keyof T]]: { [P in keyof T]: K extends T[P] ? P : never }[keyof T] }在这两种情况下,您都希望keyTextBijection接受T类型的对象并返回T & Invert<T>类型的对象。因为这样的交集看起来很丑陋,所以我通常定义一个名为Id的无操作映射类型,它具有将交集合并到单个类型对象中的效果。(此Id在this question中也称为Expand ):
type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : any;所以现在我们可以给keyTextBijection一个调用签名
function keyTextBijection<T extends Record<keyof T, S>, S extends PropertyKey>(
map: T
): Id<T & Invert<T>>;
function keyTextBijection(map: any) {
const bijection: any = {};
Object.keys(map).forEach(key => {
bijection[key] = map[key];
bijection[map[key]] = key;
});
return bijection;
}在上面的代码中,S实际上并没有做任何推理工作;它主要是给编译器一个提示,让它尝试将T类型推断为从文字到文字的映射。当您输入{a: "A"}作为map参数时,您希望编译器将类型视为{a: "A"}而不是{a: string},因为后者无法正确反转。
该函数使用单个overload调用签名,以允许实现在类型安全方面不严格,并使用any。(如果不这样做,您将发现自己不得不使用大量的type assertions,因为编译器将无法验证赋值语句的类型安全性)。
让我们测试一下:
const bij = keyTextBijection({ a: "A", b: "B", c: "C" });
/* const bij: {
a: "A";
b: "B";
c: "C";
A: "a";
B: "b";
C: "c";
} */
console.log(bij);
/* {
"a": "A",
"A": "a",
"b": "B",
"B": "b",
"c": "C",
"C": "c"
} */bij的类型和值在我看来是正确的。
https://stackoverflow.com/questions/64602432
复制相似问题