TypeScript:如何设置父类泛型默认值并解决子类赋值类型错误
问题分析与解决方案
错误原因
你遇到的报错本质是函数类型的逆变兼容性问题:当省略Parent的泛型参数时,若默认Arg和Ret设为Values,Parent的validateFunc类型会是(arg: Values) => Values——它要求能接受所有Values类型参数并返回Values类型。但ChildString的validateFunc是(arg: string) => string,仅能接受string类型参数,无法兼容更宽泛的Values参数,因此类型检查报错。
可行方案
方案1:将默认泛型参数设为never
never是所有类型的子类型,而函数参数是逆变的——因此任何Func<Arg, Ret>都可以安全赋值给Func<never, never>。修改Parent类的默认泛型参数:
type Values = string | number | boolean; type Func<Arg extends Values, Ret extends Values> = (arg: Arg) => Ret; class Parent<Arg extends Values = never, Ret extends Values = never> { constructor(public validateFunc: Func<Arg, Ret>) {} } class ChildString extends Parent<string, string> {} class ChildNumber extends Parent<number, number> {} class ChildHybrid extends Parent<string, number> {} // 此时赋值不再报错 const unknownChild: Parent = new ChildString(arg => arg);
这个方案完全保留原有类结构,无需修改子类,符合类型安全要求。需要注意:直接调用unknownChild.validateFunc时,由于参数类型是never,无法传入任何有效值——这也符合逻辑,因为你不清楚实例具体属于哪个子类,本就不该随意调用它的validateFunc。
方案2:重构为通用父接口(需谨慎使用)
如果需要后续调用unknownChild.validateFunc,可以定义包含所有子类共同结构的父接口,通过类型断言处理兼容性(前提是你能保证调用时传入的参数符合子类的Arg类型):
type Values = string | number | boolean; type Func<Arg extends Values, Ret extends Values> = (arg: Arg) => Ret; // 定义通用父接口 interface ParentLike { validateFunc: (arg: Values) => Values; } class Parent<Arg extends Values, Ret extends Values> implements ParentLike { constructor(public validateFunc: Func<Arg, Ret>) {} // 实现接口,用类型断言兼容函数类型 get validateFunc(): (arg: Values) => Values { return this.validateFunc as (arg: Values) => Values; } } class ChildString extends Parent<string, string> {} class ChildNumber extends Parent<number, number> {} class ChildHybrid extends Parent<string, number> {} // 赋值给ParentLike类型变量 const unknownChild: ParentLike = new ChildString(arg => arg);
这种方案允许调用validateFunc,但需自行承担类型风险——若传入的参数不符合子类的Arg类型,会触发运行时错误。
内容的提问来源于stack exchange,提问作者TNTzx
相关产品推荐
相关产品推荐

