TypeScript多次泛型索引后无法用字符串字面量索引类型的原因
问题:TypeScript泛型中使用字符串字面量索引嵌套类型报错的原因
问题背景
我用openapi-typescript生成了大型嵌套类型,想写一个受该类型约束的函数,接收path->method->status参数。以下是简化的示例说明问题:
报错的代码示例
type ApiSpec = { todos: { "GET": { responses: { 200: { foo: string; bar: string; }; 400: { biz: string; bash: string; }; } }, } } function fieldAt< TPath extends keyof ApiSpec, TMethod extends keyof ApiSpec[TPath], // 报错:Type '"responses"' cannot be used to index type 'ApiSpec[TPath][TMethod]'.(2536) TStatus extends keyof ApiSpec[TPath][TMethod]["responses"], > ( key: TPath, method: TMethod, status: TStatus, fieldName: keyof ApiSpec[TPath][TMethod]["responses"][TStatus] ){ }
可行的代码示例
把"responses"作为泛型参数传入后,代码能正常运行:
type ApiSpec = { todos: { "GET": { responses: { 200: { foo: string; bar: string; }; 400: { biz: string; bash: string; }; }, somethingElse: { foo: "bar" } }, } } function fieldAt< TPath extends keyof ApiSpec, TMethod extends keyof ApiSpec[TPath], // 此处正常 TSomething extends keyof ApiSpec[TPath][TMethod] & "responses", // 此处正常 TStatus extends keyof ApiSpec[TPath][TMethod][TSomething], > ( key: TPath, method: TMethod, status: TStatus, fieldName: keyof ApiSpec[TPath][TMethod][TSomething][TStatus] ){ } fieldAt("todos", "GET", 200, "foo")
报错原因分析
TypeScript在泛型约束的检查阶段,不会默认假设ApiSpec[TPath][TMethod]一定包含"responses"这个键。虽然你当前定义的ApiSpec里todos的GET方法有responses,但泛型约束只保证TMethod是ApiSpec[TPath]的合法键,却无法提前确认该方法对应的类型里必然存在"responses"属性——如果后续ApiSpec新增一个没有responses的路径或方法,第一种写法的泛型定义就会直接失效。
而第二种写法中,把"responses"作为泛型参数TSomething,并通过& "responses"约束它只能是"responses",相当于明确告诉TypeScript:我们只处理那些包含"responses"键的方法类型。此时ApiSpec[TPath][TMethod][TSomething]的访问是绝对安全的,因为TSomething已经被约束为当前方法类型中存在的键且等于"responses"。
简单总结:第一种写法里TypeScript无法确保所有ApiSpec[TPath][TMethod]都有responses,第二种写法通过泛型参数的双重约束,明确了处理范围,所以不会触发索引错误。
内容的提问来源于stack exchange,提问作者dwjohnston
相关产品推荐
相关产品推荐

