如何用映射类型扩展/合并TypeScript类的原型?
类型扩展问题:基于映射类型扩展类原型的TS类型定义
简化示例
function Base<T extends Record<string, Function>>(record: T) { class X { constructor() { } } // (A) 将record中的所有函数添加到原型中。 // 实际中我会使用`Object.defineProperty`或类似方法,此处不影响问题核心。 Object.assign(X.prototype, record) // (???) 如何编写类型,让TS知道X.prototype拥有更多成员? return X; } // (B) Derived继承自Base的返回值,同时传入`foo`和`bar`函数供Derived“继承”。 class Derived extends Base({ foo: () => 42, bar: () => "hi" }) { // (C) 我希望在此处重写函数 override bar() { return "bye"; } } const inst = new Derived(); // (D) 会打印`42, "bye"`,但TS无法识别.foo() console.log(inst.foo(), inst.bar())
当前代码在(C)和(D)处报错,因为TS无法识别(A)中对原型的修改。实际场景中Base会根据传入的record生成原型方法,需要TS识别基于T的映射类型(如Transformed<T>)。
已尝试的方案
尝试1:内部合并X的声明
试图通过声明合并让TS识别X拥有T的成员,但无法运行:
function Base<T extends Record<string, Function>>(record: T) { class X { constructor() { } } Object.assign(X.prototype, record) // 错误:Interface 'X' incorrectly extends interface 'T'. interface X extends T {} return X; }
尝试2:断言返回包含T实例的构造函数
此方案基本可用,但TS会认为T的成员属于实例而非原型,导致重写方法时报错:
function Base<T extends Record<string, Function>>(record: T) { class X { constructor() { } } Object.assign(X.prototype, record) return X as new () => X & T; }
重写时的错误:
class Derived extends Base({ foo: ():number => 42, bar: ():string => "hi" }) { // 报错:Class 'Base<{ foo: () => number; bar: () => string; }>.X & { foo: () => number; bar: () => string; }' // defines instance member property 'bar', but extended class 'Derived' defines // it as instance member function. override bar() { return "bye"; } }
虽然将函数改为字段可以解决,但会在每个实例创建新函数,无法复用原型上的函数,不够理想。
尝试3:断言返回构造函数并扩展prototype类型
此方案无法生效,TS要求Derived自行实现T,与意图不符:
function Base<T extends Record<string, Function>>(record: T) { class X { constructor() { } } Object.assign(X.prototype, record) return X as typeof X & { prototype: X & T }; }
补充完整示例
核心差异是使用映射类型Transformed<T>描述原型中实际需要的属性类型:
type Fn = (x: {}) => unknown; type Transformed<T extends Record<string, Fn>> = { // 此函数类型是原型中的实际类型。 [K in keyof T]: () => ReturnType<Fn> }
具体问题
- 一般情况下,如何用映射类型
T扩展/合并类的类型?
这类场景经常遇到:有一个计算类型T(通常是映射类型或约束泛型),需要将其与类的类型合并,上述示例是具体场景。 - 在本示例中,如何为
Base(或X)编写类型,让TS知道返回的类的原型中包含T的属性?
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

