创建 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>;
};
有没有办法解决这个问题,并应用约束而不必断言每个 keyof D
实例的类型?
答案 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
类型 Dictionary
是 built 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 的类型系统的开放类型使得这有点棘手,所以我不推荐它。