如何在typescript中别名复杂类型构造函数?

时间:2016-12-05 20:09:37

标签: oop typescript

我正在使用自定义库,对于某些类型我必须写:

 import * as pipeline from 'custom-pipeline';
 import {MapTransform} from 'custom-pipeline';

 export const createTransform = (appConfig: IAppConfig):
   MapTransform<ISomeData, IFoo> |
   MapTransform<ISomeData, IBar> => {

   switch(appConfig.createType) {
       case 'foo':
           return new pipeline.MapTransform<ISomeData, IFoo>((data: ISomeData) => {...});
       case 'bar':
           return new pipeline.MapTransform<ISomeData, IBar>((data: ISomeData) => {...});
   }
 }

特别是冗长的构造函数让我感到厌烦。我可以将这些类型改为别名:

 type FooTransform = MapTransform<ISomeData, IFoo>;
 type BarTransform = MapTransform<ISomeData, IBar>;

但我不能这样做:

 new FooTransform((data: ISomeData) => {...});
 new BarTransform((data: ISomeData) => {...});

抛出如下错误:

error TS2304: Cannot find name 'FooTransform'.

我认为这是因为我只有一个类型而不是一个类?然而,我怎样才能以我可以new FooTransform的方式对构造函数进行别名?

MapTransform的定义如下:

export declare class MapTransform<A, B> extends BaseTransform<A, B> {
    constructor(private mapFunc: (val: A) => B);
}

我可以将构造函数减少到:

fooMapFunction = (data: ISomeData): IFoo => {...};
new MapTransform<ISomeData, IFoo>(mapFunction);

虽然它与new FooTransform(fooMapFunction)不相符。

1 个答案:

答案 0 :(得分:4)

您的假设是正确的,类型声明只是编译时,因此您无法对它们执行任何操作(包括使用new实例化)。

解决方案1:输入断言

让我们假设超类看起来像这样:

class MapTransform<T1> { 
    constructor(public readonly data: T1) { /* ... */ }
}

要创建专用类型别名,您可以执行以下操作:

type TransformA = MapTransform<number>;

从超类MapTransform的角度来看,MapTransform<A>MapTransform<B>之间没有区别(这就是泛型),所以我们可以安全地分配构造函数将MapTransform等级设为常数TransformA。调用new TransformA()在运行时,与调用new MapTransform<number>()相同:

const TransformA = <{ new (data: number): TransformA; }>MapTransform;

注意类型断言?这告诉TypeScript编译器将指定的值MapTransform视为一个对象,该对象在使用TransformA实例化时构造new类型的对象。我们现在可以写:

const a = new TransformA(123);
a.data.toExponential(); // works
BTW,TypeScript编译器实际看到的是......

const TransformA = <{ new (data: number): MapTransform<number>; }>MapTransform;

...因为类型TransformA ≡ MapTransform<number>

请注意,所有这些都会评估true

  • new TransformA(123) instanceof TransformA
  • new TransformA(123) instanceof MapTransform
  • new MapTransform<number>(123) instanceof TransformA
  • new MapTransform<number>(123) instanceof MapTransform

以下是TypeScript Playground中的示例。

解决方案2:子类化

再次,让我们假设超类看起来像这样:

class MapTransform<T1> { 
    constructor(public readonly data: T1) { /* ... */ }
}

使用子类化,解决方案很简单:扩展超类并在extends子句中传递所需的类型参数。

class TransformA extends MapTransform<number> { }

Etvoilà,您现在有一个在运行时工作的构造函数以及一个在编译时工作的类型。

与第一个解决方案不同,以下两个表达式将评估true ...

  • new TransformA(123) instanceof TransformA
  • new TransformA(123) instanceof MapTransform

...虽然这些会评估false

  • new MapTransform<number>(123) instanceof TransformA
  • new MapTransform<number>(123) instanceof MapTransform

解决方案2.1:匿名子类化

如果您只需要构造函数别名但不需要类型别名,这可能会派上用场:

const TransformA = class extends MapTransform<number> { };

这称为类表达式,可以像其他表达式一样使用,例如:

class App {
    private static TransformA = class extends MapTransform<number> { };

    public doStuff() {
        return new App.TransformA(123);
    }
}

有关类型的更多信息

如果您有兴趣,可以参考以下主题:

修改

您写道,您在将这些解决方案应用于需要将函数作为参数的类时遇到了问题,因此这是另一个example in the TypeScript playground