将 InstanceType<T> 与工厂函数一起使用

Using InstanceType<T> with factory function

我有一个相当简单的工厂函数,它使用构造函数映射来根据传入的字符串参数确定要创建的正确对象类型。

我知道示例中描述的工厂模式将 class 构造函数传递给工厂,但在我的例子中,我需要传递一个简单的字符串。

class Vehicle {
    public wheels: number;
}
class Car extends Vehicle {
    public drive: number
}
class Bike extends Vehicle {
    public ride: number;
}

const CTORS = {
    car: Car,
    bike: Bike
}

type VehicleTypes = typeof CTORS;

function factory<T extends keyof VehicleTypes>(name: T): InstanceType<VehicleTypes[T]> {
    let ctor: VehicleTypes[T] = CTORS[name];

    // un-comment to see error    
    // return new ctor();

    return new ctor() as InstanceType<VehicleTypes[T]>;
}

let abc = factory('bike');
abc.ride = 5;   // type checks ok

上面的工作和类型检查没问题,但是 return 上的显式键入是避免编译器错误所必需的:

(Type 'Car | Bike' is not assignable to type 'InstanceType<{ car: typeof Car; bike: typeof Bike; }[T]>'.  Type 'Car' is not assignable to type 'InstanceType<{ car: typeof Car; bike: typeof Bike; }[T])

我怀疑打字稿抱怨 return 值不是所有潜在实例的并集。但是我无法弄清楚如何应用这些类型,以至于不需要显式键入覆盖。我还尝试了以下方法,但也无济于事:

type ReturnTypes = { 
    [P in keyof VehicleTypes]: InstanceType<VehicleTypes[P]>
}
function factory<T extends keyof VehicleTypes>(name: T): ReturnTypes[T] {
    let ctor = CTORS[name];
    return new ctor();
}

这是目前 TypeScript 的一个限制:未解析的条件类型,意味着那些依赖于尚未指定的泛型类型参数的类型,对编译器是不透明的;它真的看不到可以分配给它的某些值。类型 InstanceType<T>defined 像这样:

type InstanceType<T extends new (...args: any) => any> = 
  T extends new (...args: any) => infer R ? R : any;

这是一个条件类型, factory() 的实现中,类型 InstanceType<VehicleTypes[T]> 未解析,因为未指定 T .


有几个未解决的 GitHub 问题以及使此类未解决的条件类型更易于处理的建议,但其中 none 目前已实现(从 TS3.7 开始)。如果您足够关心去那里给他们一个或以其他方式为他们提倡,这里有一些指向他们的链接:


现在,我会说要么像您一样使用类型断言,要么找到一种不依赖条件类型的方式来表示您的类型.一种可能的方法是注意 TypeScript 中的 class 构造函数被赋予与其实例类型相同类型的 prototype 属性。这有点奇怪而且不太正确,因为实际原型不会有任何仅实例属性,但是 that's the way it is and it's unlikely to change.

因此,您可以使用以下事实制作自己的 InstanceType

type MyInstanceType<T extends { prototype: any }> = T['prototype'];

然后你可以写

function factory<T extends keyof VehicleTypes>(name: T): MyInstanceType<VehicleTypes[T]> {
    let ctor: VehicleTypes[T] = CTORS[name];
    return new ctor();
}

没有错误,你仍然得到你期望的类型检查:

let abc = factory('bike');
abc.ride = 5;   // type checks ok

希望对您有所帮助;祝你好运!

Link to code