扩展记录的通用参数不推断键类型约束

时间:2021-03-26 18:56:22

标签: typescript dictionary generics record type-inference

创建 Record 时,keyof 会正确确定其密钥类型。但是当泛型参数扩展 Record 时,不会推断出键类型约束:

export type Dictionary<K extends number | string, V> = Partial<Record<K, V>>;

type MyDict = Dictionary<string, any>;
type Key = keyof MyDict; // Key correctly infered as string;

const myFunc = <D extends MyDict>(dict: D) => {
  // keyof D incorrectly infered as string | number | symbol 
  type OtherDict = Dictionary<keyof D, number>; 
};

playground link

有没有办法解决这个问题,并应用约束而不必断言每个 keyof D 实例的类型?

1 个答案:

答案 0 :(得分:2)

编译器在技术上是正确的,D 可能具有 symbol 值键。 TypeScript 中的对象类型是开放/可扩展的,而不是封闭/精确的。 (有关确切类型的功能请求,请参阅 microsoft/TypeScript#12936)您可以向对象类型添加属性,并且仍然符合该对象类型。这是一个例子,不管它有多不可能:

const s = Symbol("symbol");
interface YourDict extends MyDict {
  [s]: number;
}
const d: YourDict = { [s]: 123 };
myFunc(d);

类型 YourDict 绝对是 MyDict 的扩展,它有一个 symbol 值键。对 myFunc() 的调用接受 YourDict 类型的值。由于 D 可能有任何键,编译器拒绝在限制为 keyof D 的地方使用 string | number


解决此问题的最简单方法是在您的 keyof D 中只允许 symbol 键:

symbol

类型 Dictionarybuilt in utility type 的同义词 export type Dictionary<K extends PropertyKey, V> = Partial<Record<K, V>>; 。这清除了错误:

PropertyKey

我的猜测是这可能足以满足您的需求,因为我怀疑您根本不想花太多时间担心 string | number | symbol 键。如果你有一些重要的理由禁止 const myFunc = <D extends MyDict>(dict: D) => { type OtherDict = Dictionary<keyof D, number>; // okay }; 或其他键,你可以采取一些步骤,但是 TypeScript 的类型系统的开放类型使得这有点棘手,所以我不推荐它。

Playground link to code