如何让TypeScript正确推断嵌套泛型中的字符串字面量类型?
让TypeScript正确推断嵌套泛型中的字符串字面量类型
问题描述
我定义了一个可任意嵌套的泛型类型,目前TypeScript会将其中的相关类型推断为string,但我希望它能推断为字符串字面量类型。相关代码如下:
// 从字符串类型派生新类型 type DeriveType<T extends string, Base extends string> = `${Base}${T}`; // 可任意嵌套的类型 type Nested<T extends string, Base extends string = ''> = { value: T; derived(path: DeriveType<T, Base>): void; children?: Nested<string, `${Base}${T}`>[]; // 这里需要修改 }; declare function makeNested<T extends string>(nested: Nested<T>): Nested<T>; makeNested({ value: '/parent', // value类型为 "/parent" derived(path) {}, // path正确推断为 "/parent" children: [ { value: '/child', // value类型被推断为string,而非"/child" derived(path) {}, // path被错误推断为 `/parent${string}` // 期望推断为 "/parent/child" }, ], });
解决方案
问题核心在于Nested<string, ${Base}${T}>的定义:直接使用string会让TypeScript放弃字面量类型推断,转而使用宽泛的string类型。我们需要调整类型定义,让TypeScript能递归捕获每个层级的字面量类型。
修改后的代码
// 保留路径拼接逻辑 type DeriveType<T extends string, Base extends string> = `${Base}${T}`; // 重新定义递归嵌套类型,自动推断子节点字面量 type Nested<Base extends string = ''> = { value: string; derived(path: DeriveType<this['value'], Base>): void; children?: Nested<`${Base}${this['value']}`>[]; }; // 调整函数声明,让TypeScript自动推断整个嵌套结构类型 declare function makeNested<T extends Nested>(nested: T): T; // 现在所有层级的类型都会被正确推断 makeNested({ value: '/parent', // 类型为 "/parent" derived(path) {}, // path 推断为 "/parent" children: [ { value: '/child', // 类型为 "/child" derived(path) {}, // path 推断为 "/parent/child" children: [ { value: '/grandchild', // 类型为 "/grandchild" derived(path) {} // path 推断为 "/parent/child/grandchild" } ] } ], });
原理说明
- 将
Nested的泛型参数调整为仅跟踪当前基础路径Base,不再绑定顶层的T,让每个节点的类型推断独立且递归。 - 使用
this['value']引用当前节点的value字面量类型,配合DeriveType生成精准的路径类型。 - 子节点的
Nested泛型参数自动拼接当前路径与当前节点的value,形成新的基础路径,实现层级间的类型传递。 makeNested函数直接接受Nested类型参数,让TypeScript自动推断整个嵌套结构的字面量类型,而非仅推断顶层类型。
更简洁的写法
如果不需要单独定义DeriveType,可以直接在类型中拼接路径:
type Nested<Base extends string = ''> = { value: string; derived(path: `${Base}${this['value']}`): void; children?: Nested<`${Base}${this['value']}`>[]; }; declare function makeNested<T extends Nested>(nested: T): T;
内容的提问来源于stack exchange,提问作者Allan J.
相关产品推荐
相关产品推荐

