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

 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)不相称。

最佳答案

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

解决方案1:类型断言

让我们假设父类(super class)看起来像这样:

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

要创建专门的类型别名,可以执行以下操作:
type TransformA = MapTransform<number>;

从父类(super class)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

顺便说一句,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:子类化

    再次,让我们假设父类(super class)看起来像这样:
    class MapTransform<T1> {
        constructor(public readonly data: T1) { /* ... */ }
    }
    

    使用子类,解决方案很简单:扩展父类(super class),并在extends子句中传递所需的类型参数。
    class TransformA extends MapTransform<number> { }
    

    等等,您现在有了一个在运行时可以使用的构造函数以及一个在编译时可以使用的类型。

    与第一种解决方案不同,以下两个表达式将评估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);
        }
    }
    

    有关类型的更多信息

    如果您有兴趣,这里是有关此主题的更多链接:
  • Named Types in TypeScript Spec
  • Type Assertions
  • TypeScript Handbook: Interfaces,特别是“类的静态端与实例端之间的区别”一节
  • Class Expressions


  • 编辑

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

    10-08 02:12