如何在打字稿中为复杂类型构造函数取别名?

k0p*_*kus 5 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) => {...});
   }
 }
Run Code Online (Sandbox Code Playgroud)

尤其是冗长的构造函数让我很恼火。我可以对类型进行别名:

 type FooTransform = MapTransform<ISomeData, IFoo>;
 type BarTransform = MapTransform<ISomeData, IBar>;
Run Code Online (Sandbox Code Playgroud)

但我不能这样做:

 new FooTransform((data: ISomeData) => {...});
 new BarTransform((data: ISomeData) => {...});
Run Code Online (Sandbox Code Playgroud)

抛出一个错误,如:

error TS2304: Cannot find name 'FooTransform'.
Run Code Online (Sandbox Code Playgroud)

我认为这是因为我只有一个类型而不是一个类?然而,我如何才能new FooTransform以如上所述的方式为构造函数设置别名?

的定义MapTransform看起来像:

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

我可以将构造函数简化为:

fooMapFunction = (data: ISomeData): IFoo => {...};
new MapTransform<ISomeData, IFoo>(mapFunction);
Run Code Online (Sandbox Code Playgroud)

尽管它与new FooTransform(fooMapFunction).

Fab*_*uer 7

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

解决方案 1:类型断言

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

class MapTransform<T1> { 
    constructor(public readonly data: T1) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

要创建专门的类型别名,您可以这样做:

type TransformA = MapTransform<number>;
Run Code Online (Sandbox Code Playgroud)

从超类的角度来看,,等等MapTransform之间没有区别(这是泛型的点),因此我们可以安全地将 class 的构造函数分配给一个常量。调用时,在运行时,相同的调用:MapTransform<A>MapTransform<B>MapTransformTransformAnew TransformA()new MapTransform<number>()

const TransformA = <{ new (data: number): TransformA; }>MapTransform;
Run Code Online (Sandbox Code Playgroud)

注意到类型断言了吗?这告诉 TypeScript 编译器将分配的值MapTransform视为一个对象,该对象在TransformA使用new. 我们现在可以写:

const a = new TransformA(123);
a.data.toExponential(); // works
Run Code Online (Sandbox Code Playgroud)

顺便说一句,TypeScript 编译器实际看到的是这个......

const TransformA = <{ new (data: number): MapTransform<number>; }>MapTransform;
Run Code Online (Sandbox Code Playgroud)

...因为类型 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) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

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

class TransformA extends MapTransform<number> { }
Run Code Online (Sandbox Code Playgroud)

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

与第一个解决方案不同,以下 2 个表达式将评估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> { };
Run Code Online (Sandbox Code Playgroud)

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

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

    public doStuff() {
        return new App.TransformA(123);
    }
}
Run Code Online (Sandbox Code Playgroud)

更多关于类型

如果您有兴趣,这里有更多关于该主题的链接:


编辑

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