TypeScript扩展类加属性后报TS2394重载与实现签名不兼容怎么办?
错误原因
这个报错是TypeScript的正常类型校验逻辑,不是bug,核心触发点是函数参数的逆变特性:
你的重载签名中,buildFields回调的入参是PostReturnTypeBuilder/UserReturnTypeBuilder这类子类实例,但实现签名里buildFields的入参定义为父类GraphtonBaseReturnTypeBuilder实例。TypeScript中函数参数是逆变的,也就是说如果某个位置要求传入「可接收父类实例的函数」,你不能传入「只能接收子类实例的函数」——后者可能访问子类独有的属性,如果运行时真的传入父类实例就会触发报错。
至于删除protected anything = 'test'后错误消失,是因为TypeScript采用结构化类型系统:如果子类和父类的属性、方法结构完全一致,TS会判定二者类型兼容,逆变校验就可以通过;一旦子类新增了独有的属性,二者结构不等,兼容性校验就会失败,抛出TS2394错误。
解决方案
针对你做代码生成插件的场景,推荐两种改动方案:
方案1:实现层用any绕过类型校验(推荐)
你对外暴露的重载签名已经保证了调用侧的类型安全,只需要把实现签名的回调参数类型改为any即可,完全不影响外部使用时的类型提示:
abstract class GraphtonBaseReturnTypeBuilder { public withRelated(relatedType: string, buildFields: (r: GraphtonBaseReturnTypeBuilder) => void): this { return this; } } type UserReturnTypeObjectField = "posts"|"friends"; class UserReturnTypeBuilder extends GraphtonBaseReturnTypeBuilder { protected anything = 'test'; public withRelated(relatedType: "posts", buildFields: (r: PostReturnTypeBuilder) => void): this; public withRelated(relatedType: "friends", buildFields: (r: UserReturnTypeBuilder) => void): this; // 仅修改实现签名的buildFields参数类型为any public withRelated(relatedType: UserReturnTypeObjectField, buildFields: (r: any) => void): this { return super.withRelated(relatedType, buildFields); } } type PostReturnTypeObjectField = "author"|"repatedPosts"; class PostReturnTypeBuilder extends GraphtonBaseReturnTypeBuilder { public withRelated(relatedType: "author", buildFields: (r: UserReturnTypeBuilder) => void): this; public withRelated(relatedType: "repatedPosts", buildFields: (r: PostReturnTypeBuilder) => void): this; // 仅修改实现签名的buildFields参数类型为any public withRelated(relatedType: PostReturnTypeObjectField, buildFields: (r: any) => void): this { return super.withRelated(relatedType, buildFields); } }
方案2:改造基类为泛型基类(类型更安全)
如果不想用any,可以给基类增加泛型参数限定回调入参类型:
// 基类增加泛型参数 abstract class GraphtonBaseReturnTypeBuilder<T = GraphtonBaseReturnTypeBuilder<any>> { public withRelated(relatedType: string, buildFields: (r: T) => void): this { return this; } } type UserReturnTypeObjectField = "posts"|"friends"; // 子类继承时传入所有可能的回调入参类型的联合 class UserReturnTypeBuilder extends GraphtonBaseReturnTypeBuilder<PostReturnTypeBuilder | UserReturnTypeBuilder> { protected anything = 'test'; public withRelated(relatedType: "posts", buildFields: (r: PostReturnTypeBuilder) => void): this; public withRelated(relatedType: "friends", buildFields: (r: UserReturnTypeBuilder) => void): this; public withRelated(relatedType: UserReturnTypeObjectField, buildFields: (r: GraphtonBaseReturnTypeBuilder) => void): this { return super.withRelated(relatedType, buildFields); } }
内容的提问来源于stack exchange,提问作者Roboroads
相关产品推荐
相关产品推荐

