首页 > 解决方案 > 扩展记录的通用参数不推断键类型约束

问题描述

创建 时Recordkeyof正确确定其键类型。但是当泛型参数扩展 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实例的类型?

标签: typescriptdictionarygenericsrecordtype-inference

解决方案


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

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

该类型YourDict绝对是 的扩展MyDict,并且它具有symbol-valued 键。并且调用myFunc()接受 type 的值YourDict。由于D可能有任何键,编译器不愿在keyof D受限于string | number.


解决此问题的最简单方法是在不尝试分开keyof D排除symbol的情况下,只允许symbol您的键Dictionary

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
};

我的猜测是,这可能足以满足您的需求,因为我怀疑您根本不想花很多时间担心symbol钥匙。如果你有一些重要的原因禁止symbol或其他键,你可以采取一些步骤,但是 TypeScript 类型系统的开放类型使得这有点棘手,所以我不推荐它。

Playground 代码链接


推荐阅读