TypeScript中如何在基类方法签名中引用调用类类型
解决TypeScript基类中使用this类型实现通用修改方法的类型错误
我尝试利用TypeScript的this类型特性,在基类中实现一个通用的toModified方法,让子类继承后自动适配自身类型,避免重复代码。但基类中的实现出现了类型错误,子类重写后却能正常运行。
问题代码
type ClassProperties<C> = { [K in keyof C as C[K] extends Function ? never : K]: C[K] } abstract class BaseClass { clone() { return structuredClone(this); } toModified(change: Partial<ClassProperties<this>>) { const c = this.clone(); type changetype = typeof change; type changekeys = keyof changetype; type changevals = changetype[changekeys]; Object.entries(change).forEach(([k, v]: [changekeys, changevals]) => { (c[k] as changevals) = v; }); return c; } } export class ConcreteClass extends BaseClass { constructor(public a: number, public b: string = "", public c: boolean = false) { super(); } }
错误信息
typescript [2345]: Argument of type '([k, v]: [keyof ClassProperties<this>, changevals]) => void' is not assignable to parameter of type '(value: [string, unknown], index: number, array: [string, unknown][]) => void'.
原因分析
在基类中,this是一个抽象的动态类型,TypeScript无法在编译阶段确定ClassProperties<this>的具体键类型。而Object.entries(change)返回的是[string, unknown][],导致回调函数的参数类型与预期不匹配。子类中明确指定了具体类名,TypeScript能确定属性键的具体类型,因此不会报错。
解决方案
方案一:调整类型断言
直接在遍历回调中对键和值做精确类型断言,让TypeScript识别类型兼容性:
type ClassProperties<C> = { [K in keyof C as C[K] extends Function ? never : K]: C[K] } abstract class BaseClass { clone() { return structuredClone(this); } toModified(change: Partial<ClassProperties<this>>) { const c = this.clone(); Object.entries(change).forEach(([k, v]) => { const propKey = k as keyof ClassProperties<this>; c[propKey] = v as ClassProperties<this>[typeof propKey]; }); return c; } } export class ConcreteClass extends BaseClass { constructor(public a: number, public b: string = "", public c: boolean = false) { super(); } }
方案二:使用泛型约束this
通过泛型明确this的具体子类类型,提升类型检查的准确性:
type ClassProperties<C> = { [K in keyof C as C[K] extends Function ? never : K]: C[K] } abstract class BaseClass { clone() { return structuredClone(this); } toModified<T extends BaseClass>(this: T, change: Partial<ClassProperties<T>>): T { const c = this.clone() as T; Object.entries(change).forEach(([k, v]) => { const propKey = k as keyof ClassProperties<T>; c[propKey] = v as ClassProperties<T>[typeof propKey]; }); return c; } } export class ConcreteClass extends BaseClass { constructor(public a: number, public b: string = "", public c: boolean = false) { super(); } }
两种方案都能实现基类的通用性,子类无需重写toModified方法,同时保留类型安全。
内容的提问来源于stack exchange,提问作者Camden Narzt
相关产品推荐
相关产品推荐

