访问泛型推断类型的子属性时如何防止类型拓宽?
TypeScript泛型类中私有属性的类型推断异常问题
代码示例
export type Expect<T extends true> = T; export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false; export class Example<T extends { a: boolean }> { #param!: T["a"] extends true ? { a: number } : { a: string }; get wtf() { return this.#param.a; } get thisMakesSense() { return this.#param; } } const ex = new Example<{ a: true }>(); type _Test1 = Expect<Equal<typeof ex.thisMakesSense, { a: number }>>; type _Test2 = Expect<Equal<typeof ex.wtf, number>>; // 错误,实际类型为number | string。这是为什么???
问题说明
我原本期望ex.wtf的类型是number,但实际推断结果却是number | string。尝试给get wtf()添加显式返回类型时,还会报错,提示this.#param.a无法赋值给对应条件类型的返回类型。目前只能靠类型断言解决,但这样会掩盖潜在问题。
原因分析
这是因为TypeScript在处理泛型类中私有属性的嵌套属性访问时,没法正确跟踪泛型参数T的具体类型对条件类型分支的影响。虽然this.#param的类型能被正确推断为{a: number}(从_Test1能通过可以看出来),但当访问this.#param.a时,类型检查器没把这个属性访问和原始的条件类型分支关联起来,直接退化为条件类型分支的联合结果number | string。
简单说就是,私有属性的类型在类内部是明确的,但TypeScript的类型推断系统处理这种嵌套的条件类型属性访问时存在局限,没法把泛型参数的具体值传递到属性的类型推断里。
解决方案
方案一:把私有属性类型提升为类的泛型参数
给类添加额外的泛型参数来明确跟踪#param的类型,让TypeScript能准确推断:
export class Example<T extends { a: boolean }, P = T["a"] extends true ? { a: number } : { a: string }> { #param!: P; get wtf(): P["a"] { return this.#param.a; } get thisMakesSense(): P { return this.#param; } } const ex = new Example<{ a: true }>(); type _Test1 = Expect<Equal<typeof ex.thisMakesSense, { a: number }>>; // 正常通过 type _Test2 = Expect<Equal<typeof ex.wtf, number>>; // 正常通过
方案二:使用带条件类型的类型断言
如果不想修改类的泛型结构,可以用基于泛型参数的条件类型做断言,比盲目断言更安全,同时标注注释说明原因:
get wtf() { // 基于泛型参数的条件类型断言,确保类型匹配 return this.#param.a as T["a"] extends true ? number : string; }
内容的提问来源于stack exchange,提问作者Thomas G. Lopes
相关产品推荐
相关产品推荐

