TypeScript为何不遵循泛型方法中预期的参数类型约束?
TypeScript泛型类push方法的类型约束未生效问题
我编写了以下TypeScript代码,期望在传入不符合泛型约束的参数时触发类型错误,但实际并未报错:
class AB<Initial, Current = Initial> { array: AB<unknown, unknown>[] = [] push<Next extends any>(ab: AB<Current, Next>): AB<Initial, Next> { this.array.push(ab) return this as AB<Initial, Next>; } } // ab的类型为AB<'a','b'>,调用push时应接收AB<'b', Next>类型的参数 const ab = new AB<'a', 'b'>(); // 此处传入AB<'e', 'x'>应触发错误('e'不等于'b'),但无报错 const ax = ab.push(new AB<'e', 'x'>); // ax的类型为AB<'a','x'>,调用push时应接收AB<'x', Next>类型的参数,传入AB<'f','z'>应报错但未触发 const az = ax.push(new AB<'f', 'z'>);
问题原因
TypeScript的类型兼容性检查中,如果泛型类的某个泛型参数(此处为Initial)没有在类的成员中被实际引用,类型系统会忽略该泛型参数的差异,认为不同Initial但Current/Next匹配的AB实例是兼容的。由于原代码中Initial仅作为泛型声明存在,未在类的属性或方法逻辑中被使用,因此TypeScript不会检查参数的Initial是否与当前实例的Current匹配。
修复方案
要让TypeScript检查Initial参数的匹配,需要在类中添加一个依赖Initial类型的成员,让Initial成为类类型的一部分。例如添加一个只读属性标记Initial类型:
class AB<Initial, Current = Initial> { // 添加只读属性,让Initial纳入类型检查范围 readonly _initialMarker: Initial; array: AB<unknown, unknown>[] = [] constructor() { // 类型断言赋值,仅用于标记类型,无需实际值 this._initialMarker = undefined as unknown as Initial; } push<Next extends any>(ab: AB<Current, Next>): AB<Initial, Next> { this.array.push(ab) return this as AB<Initial, Next>; } } const ab = new AB<'a', 'b'>(); // 现在会触发类型错误:AB<'e','x'> 不可赋值给 AB<'b','x'> const ax = ab.push(new AB<'e', 'x'>); // 同样触发错误:AB<'f','z'> 不可赋值给 AB<'x','z'> const az = ax.push(new AB<'f', 'z'>);
添加_initialMarker属性后,TypeScript会将Initial作为类类型的一部分进行校验,当传入参数的Initial与当前实例的Current不匹配时,就会触发预期的类型错误。
内容的提问来源于stack exchange,提问作者remix23
相关产品推荐
相关产品推荐

