将InstanceType <T>与工厂功能一起使用

时间:2019-10-25 13:13:16

标签: typescript typescript-generics

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

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

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

上面的方法和类型检查正常,但是为避免编译器错误,必须在返回值上进行显式键入:

(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])

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

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();
}

1 个答案:

答案 0 :(得分:1)

目前这是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问题,其中有一些建议使这些未解决的条件类型更易于处理,但目前都没有实现(从TS3.7开始)。如果您足够关心他们的到来并给他们a或以其他方式提倡他们,那么这里有一些与他们的链接:


就目前而言,我想要么像您一样使用类型断言,要么找到一种不依赖条件类型的方式表示您的类型的方法。前进的一种可能方法是,注意TypeScript中的类构造函数被赋予与实例类型相同类型的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