如何在TypeScript中编写生成双射的函数的函数签名

  • 本文关键字:函数 TypeScript typescript
  • 更新时间 :
  • 英文 :


我有一个函数,它获取一个映射并生成映射的双射:

export function keyTextBijection(map) {
const bijection = {};
Object.keys(map).forEach(key => {
bijection[key] = map[key];
bijection[map[key]] = key;
});
return bijection;
}

如何在TypeScript中优雅地为函数签名?

它有助于编写一个类型函数,将类似{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在这个问题中也被称为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},因为后者不会正确反转。

该函数使用单个重载调用签名,以允许实现在类型安全方面松懈,并使用any。(如果不这样做,您将发现自己不得不使用一堆类型断言,因为编译器将无法验证赋值语句的类型安全性(。


让我们测试一下:

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的类型和值在我看来是正确的。


到代码的游乐场链接

最新更新