泛型场景下TypeScript提示Record无法索引的原因及修正方案
TypeScript泛型索引报错原因及解决方法
原问题代码
type Foo<K extends string, T extends Record<K, string>> = { keys: K value: T } function bar<T extends Foo<'1' | '2', { 1: 'a', 2: 'b' }>>(def: T['value'], key: T['keys']) { // Type 'T["keys"]' cannot be used to index type 'T["value"]'.(2536) return def[key]; }
报错原因
TypeScript的类型系统无法追踪T['keys']和T['value']之间的原始约束关联。当使用T extends Foo<'1' | '2', { 1: 'a', 2: 'b' }>时,TypeScript仅检查T的结构是否匹配Foo的实例类型,不会保留Foo泛型参数间的约束(即value必须是Record<keys, string>)。
这意味着TypeScript会认为存在这样的T:它结构上符合Foo的子类型要求,但T['keys']并非T['value']的有效索引(比如{ keys: '1', value: { 2: 'b' } },虽然它违反了Foo的原始泛型约束,但结构上属于Foo<'1' | '2', { 1: 'a', 2: 'b' }>的子类型),因此会抛出索引不合法的错误。
正确代码示例
方案1:直接关联索引键与值类型
通过泛型参数K直接绑定键类型和对应的值类型,让TypeScript明确两者的索引关系:
type Foo<K extends string, T extends Record<K, string>> = { keys: K value: T } function bar<K extends '1' | '2'>(def: Record<K, string>, key: K) { return def[key]; }
方案2:基于完整的Foo类型约束
如果需要保留Foo的结构传递,直接使用完整的Foo对象作为参数,TypeScript能正确识别内部属性的约束关系:
type Foo<K extends string, T extends Record<K, string>> = { keys: K value: T } function barFromFoo<K extends string, V extends Record<K, string>>(foo: Foo<K, V>) { return foo.value[foo.keys]; } // 调用示例 const foo1: Foo<'1', { 1: 'a' }> = { keys: '1', value: { 1: 'a' } }; console.log(barFromFoo(foo1)); // 类型为'a' const foo2: Foo<'1' | '2', { 1: 'a', 2: 'b' }> = { keys: '1' | '2', value: { 1: 'a', 2: 'b' } }; console.log(barFromFoo(foo2)); // 类型为'a' | 'b'
内容的提问来源于stack exchange,提问作者Dean Xu
相关产品推荐
相关产品推荐

