TypeScript:是否可以在不使用接口继承或实现的情况下将一个 class 的静态 属性 与另一个 class 混合?

TypeScript: Is it possible to mix a static property of one class with another class without using inheritance or implementation of interface?

我希望 TypeScript 的 Intellisense 能够识别通过 interface 继承的 static 属性,而无需在具体的 class 中应用继承或实现。可能吗?


interface Interface2 {

    instancePropertyRecognizedByInterfaceInheritance: string;

}

class Interface1 {

    constructor() {

    }

    instanceMethodRecognizedByInterfaceInheritance(): void {

    }

}

class InheritsFromAll {

    static inheritedClassProperty: string = `class property defined in ${this.name}`;

}

// interfaces permit multiple inheritance even from another class
interface InheritsFromAll extends 
    Interface1,
    Interface2
    {

}

// it works only for instance properties
interface MixedClass extends InheritsFromAll {

}

class MixedClass {
    
}

const mixedClass: MixedClass = new MixedClass();

// recognized
mixedClass.instanceMethodRecognizedByInterfaceInheritance();

// not recognized
MixedClass.inheritedClassProperty;

要回答所问的问题,目前没有办法使用 declaration merging to modify the static side of a class. There's a longstanding open feature request at microsoft/TypeScript#2957 寻求一种方法来做到这一点,但在可预见的未来,只有解决方法。该问题中提到的一个是声明一个 namespace/module 与 class 构造函数同名,并分别声明 export 静态:

namespace MixedClass {
  export const inheritedClassProperty = InheritsFromAll.inheritedClassProperty;
}
const mixedClass: MixedClass = new MixedClass();

MixedClass.inheritedClassProperty;

对于某些人来说,这足以满足他们的用例。


如果它对您不起作用,最好的方法是退后一步,尝试考虑底层用例,以及是否存在完全不涉及声明合并的解决方案。如果你只想使用一个 class 的静态作为另一个 class 的混合,你可以创建一个函数,它同时接受混合和目标 class 构造函数和 returns 目标 class 构造函数作为原始构造函数和混合构造函数的静态部分的 intersection,如下所示:

const MixStatics = <T, U>(mixin: T, ctor: U) => Object.assign(ctor, mixin) as
    U & { [K in keyof T as Exclude<K, "prototype">]: T[K] };

这会产生类似 U & T 的类型,其中 U 是目标 class 构造函数的类型 ctorTmixin 静力学。但是我们不想复制 prototype 属性 或 construct signature from T into the output, since the Object.assign() function won't copy those. Hence the key-remapped type.

我们来测试一下:

class StaticsToMixIn {
    static staticProp: string = `class property defined in ${this.name}`;
    static prop1 = 1;
    static prop2 = 2;
    /*..*/
    static prop100 = 100;
}

const MixedClass = MixStatics(StaticsToMixIn, class MixedClass {
    static anotherStaticProp: string = `class property defined in ${this.name}`;
});

/* const MixedClass: typeof MixedClass & {
    staticProp: string;
    prop1: number;
    prop2: number;
    prop100: number;
} */

type MixedClass = InstanceType<typeof MixedClass>

根据编译器,MixedClass 是与作为 MixStatics 的第二个参数传递的 class 表达式相同类型的 class 构造函数,以及包含来自 StaticsToMixin.

的所有静态的对象类型

而且运行时的运行方式似乎相似:

console.log(MixedClass.staticProp) // "class property defined in StaticsToMixIn" 
console.log(MixedClass.prop1) // 1
console.log(MixedClass.prop2) // 2
/*..*/
console.log(MixedClass.prop100) // 100
console.log(MixedClass.anotherStaticProp); // "class property defined in MixedClass" 

MixStatics()完美吗?可能不会;当您在运行时和类型系统中将内容从一个构造函数复制到另一个构造函数时,可能会出现很多边缘情况。但至少这有可能以编程方式将 classes 的静态端合并在一起,而声明合并目前无法做到。

Playground link to code