TypeScript中无法将keyof EntityType赋值为key in keyof EntityType对象的键
我定义了一个接受对象类型*EntityType*的泛型函数useSorting,第一个参数defaultSortColumn必须是*EntityType*的键。比如当*EntityType*是User类型时:
type User = { name: string; age: number; } const user: User = { name: 'Scott Pilgrim', age: 24, };
函数第一个参数只能传入name或age这两个字符串值。
第二个参数defaultSortDirection是SortDirections枚举的取值:
enum SortDirections { ASC = 'ASC', DESC = 'DESC', }
我想在函数内部创建一个columns对象,它的属性键是*EntityType*的键,属性值是SortDirections的取值,于是写了下面的代码:
export const useSorting = <EntityType>(defaultSortColumn: keyof EntityType, defaultSortDirection: SortDirections = SortDirections.DESC) => { const columns: { [key in keyof EntityType]?: SortDirections } = { [defaultSortColumn]: defaultSortDirection }; // ... };
但{ [defaultSortColumn]: defaultSortDirection }被TypeScript识别为{ [x: string]: SortDirections; }类型,不是预期的{ [key in keyof EntityType]?: SortDirections },导致报错:
Type '{ [x: string]: SortDirections; }' is not assignable to type '{ [key in keyof EntityType]?: SortDirections | undefined; }'.ts(2322)
请问有没有不使用as类型断言的方式,为这个对象的键正确标注类型?
解决方案
可以通过给defaultSortColumn添加独立的泛型参数,让TypeScript明确识别动态键的具体类型,不需要使用as断言:
enum SortDirections { ASC = 'ASC', DESC = 'DESC', } export const useSorting = <EntityType, K extends keyof EntityType>( defaultSortColumn: K, defaultSortDirection: SortDirections = SortDirections.DESC ) => { const columns: Partial<Record<keyof EntityType, SortDirections>> = { [defaultSortColumn]: defaultSortDirection }; // 后续逻辑... };
原理说明
原来的写法中,defaultSortColumn的类型是keyof EntityType(一个联合类型),TypeScript在处理动态键时会默认将其宽泛地推断为string,导致生成的对象类型不匹配。
通过新增泛型参数K extends keyof EntityType,让TypeScript明确defaultSortColumn是keyof EntityType的具体子类型,此时动态生成的对象会被推断为{ [P in K]: SortDirections },这个类型完全兼容Partial<Record<keyof EntityType, SortDirections>>(和原代码中的{ [key in keyof EntityType]?: SortDirections }等价),因此不会出现类型错误。
内容的提问来源于stack exchange,提问作者Igor Nikiforov

