TypeScript类型定义可正常工作却报错的问题排查
我定义了如下只读常量对象:
const someObject = { topLevelKey1: { midLevelKeyA: { someProp: ['a', 'b'], }, }, topLevelKey2: { midLevelKeyB: { someProp: ['c', 'd', 'e'], }, midLevelKeyC: { someProp: ['g'], }, }, } as const;
我需要定义一个函数,签名形如someFunction(topLevelKey, midLevelKey, objectWithKeysMatchingToProp),合法调用示例为someFunction('topLevelKey2', 'midLevelKeyB', {c: 2, d: 3, e: 4})——第三个参数的键必须严格匹配对应层级下someProp数组中的元素。
为了实现第三个参数的类型约束,我写了如下泛型类型:
type X< T extends keyof typeof someObject, K extends keyof (typeof someObject)[T] > = { [key in (typeof someObject)[T][K]['someProp'][number]]: number; }; const a: X<'topLevelKey2', 'midLevelKeyC'> = { f: 3 // 此处会报错,正确键应为'g' };
奇怪的是,这个类型实际功能完全正常:能自动补全对象键、键错误时会抛出正确的类型错误,但TypeScript却在(typeof someObject)[T][K]['someProp'][number]这段代码上抛出了以下错误:
Type '"someProp"' cannot be used to index type '{ readonly topLevelKey1: { readonly midLevelKeyA: { readonly prop1: readonly []; readonly someProp: readonly ["a", "b"]; }; }; readonly topLevelKey2: { readonly midLevelKeyB: { readonly prop1: readonly []; readonly someProp: readonly ["c", "d", "e"]; }; readonly midLevelKeyC: { ...; }; }; }[T][K]'.
Type '{ readonly topLevelKey1: { readonly midLevelKeyA: { readonly prop1: readonly []; readonly someProp: readonly ["a", "b"]; }; }; readonly topLevelKey2: { readonly midLevelKeyB: { readonly prop1: readonly []; readonly someProp: readonly ["c", "d", "e"]; }; readonly midLevelKeyC: { ...; }; }; }[T][K]["someProp"][number]' is not assignable to type 'string | number | symbol'.Type '{ readonly topLevelKey1: { readonly midLevelKeyA: { readonly prop1: readonly []; readonly someProp: readonly ["a", "b"]; }; }; readonly topLevelKey2: { readonly midLevelKeyB: { readonly prop1: readonly []; readonly someProp: readonly ["c", "d", "e"]; }; readonly midLevelKeyC: { ...; }; }; }[T][keyof { ...; }[T]]["so...' is not assignable to type 'string | number | symbol'.
Type '{ readonly topLevelKey1: { readonly midLevelKeyA: { readonly prop1: readonly []; readonly someProp: readonly ["a", "b"]; }; }; readonly topLevelKey2: { readonly midLevelKeyB: { readonly prop1: readonly []; readonly someProp: readonly ["c", "d", "e"]; }; readonly midLevelKeyC: { ...; }; }; }[T][string]["someProp"][nu...' is not assignable to type 'string | number | symbol'.
Type '{ readonly topLevelKey1: { readonly midLevelKeyA: { readonly prop1: readonly []; readonly someProp: readonly ["a", "b"]; }; }; readonly topLevelKey2: { readonly midLevelKeyB: { readonly prop1: readonly []; readonly someProp: readonly ["c", "d", "e"]; }; readonly midLevelKeyC: { ...; }; }; }[T][string]["someProp"][nu...' is not assignable to type 'string | number | symbol'.Type 'number' cannot be used to index type '{ readonly topLevelKey1: { readonly midLevelKeyA: { readonly prop1: readonly []; readonly someProp: readonly ["a", "b"]; }; }; readonly topLevelKey2: { readonly midLevelKeyB: { readonly prop1: readonly []; readonly someProp: readonly ["c", "d", "e"]; }; readonly midLevelKeyC: { ...; }; }; }[T][K]["someProp"]'.
添加@ts-ignore可以忽略报错并获得正常的类型安全,但这显然不是合理的解决方案。请问这个报错的原因是什么?
另外需要注意:当移除中间层的K泛型参数时,代码完全正常,不会抛出任何错误。
内容的提问来源于stack exchange,提问作者David Mulder

