如何正确编写函数签名以接受union-typed参数?

时间:2017-07-25 20:35:17

标签: typescript

我正在尝试为API提供一个函数,该函数接受可以是A或B类型的参数。我使用interfaces来键入参数。这些类型不共享一个成员。我的尝试导致编译器抱怨x类型上没有属性x。

export interface MyTypeA {
  prop1: string;
  prop2: boolean;
} 
export interface MyTypeB {
  prop3: number;
  prop4: string;
}

doSomething(param1: string, param2: MyTypeA | MyTypeB){
  switch(param1){
    case 'a':
    case 'b': {
      const cf = this.resolver.resolveComponentFactory(MyClassAComponent);
      const component = this.wrap.createComponent(cf);
      component.instance.prop1 = param2.prop1;
      component.instance.prop2 = param2.prop2;
      break;
    }
    case 'c': {
      const cf = this.resolver.resolveComponentFactory(MyClassBComponent);
      const component = this.wrap.createComponent(cf);
      component.instance.prop3 = param2.prop3;
      break;
    }

  }
}

我不确定interface可以做到这一点,我想我可能不得不使用type,但不知道如何做。

1 个答案:

答案 0 :(得分:4)

我看到了3种可能的解决方案:

如果您将param1param2结合起来,我会选择Tagged Unions。否则使用用户定义的类型保护。

转换param2的每一次使用:

如果你不得不多次使用它,我觉得太冗长,但是最直接的解决方案:

...

component.instance.prop1 = (param2 as MyTypeA).prop1;
component.instance.prop2 = (param2 as MyTypeA).prop2;

...

此解决方案不会生成其他代码(演员表将从生成的代码中完全删除)。

使用标记联合(也称为区分联合或代数数据类型):

您可以合并param1param2并将自定义类型转换为tagged unions

export interface MyTypeA {
  param1: 'a' | 'b';
  prop1: string;
  prop2: boolean;
}

export interface MyTypeB {
  param1: 'c';
  prop3: number;
  prop4: string;
}

doSomething(param2: MyTypeA | MyTypeB) {
  switch(param2.param1) {
    case 'a':
    case 'b': {
      // The compiler knows param2 is of type MyTypeA, because its param1
      // property is either 'a' or 'b'.

      const cf = this.resolver.resolveComponentFactory(MyClassAComponent);
      const component = this.wrap.createComponent(cf);

      component.instance.prop1 = param2.prop1;
      component.instance.prop2 = param2.prop2;

      break;
    }

    case 'c': {
      // The compiler knows param2 is of type MyTypeB, because its param1
      // property is 'c'.

      const cf = this.resolver.resolveComponentFactory(MyClassBComponent);
      const component = this.wrap.createComponent(cf);

      component.instance.prop3 = param2.prop3;

      break;
    }
  }
}

此解决方案不会生成其他代码(接口,包括标记的参数,将不会出现在生成的代码中)。

使用用户定义的类型防护:

您可以使用User Defined Type Guards缩小param2的类型:

export interface MyTypeA {
  prop1: string;
  prop2: boolean;
}

export interface MyTypeB {
  prop3: number;
  prop4: string;
}

function isA(arg: any): arg is MyTypeA {
    return arg.hasOwnProperty('prop1');
}

function isB(arg: any): arg is MyTypeB {
    return arg.hasOwnProperty('prop3');
}

doSomething(param1: string, param2: MyTypeA | MyTypeB) {
  switch(param1) {
    case 'a':
    case 'b': {
      if (!isA(param2)) return;

      // The compiler knows param2 is of type MyTypeA:

      const cf = this.resolver.resolveComponentFactory(MyClassAComponent);
      const component = this.wrap.createComponent(cf);

      component.instance.prop1 = param2.prop1;
      component.instance.prop2 = param2.prop2;

      break;
    }

    case 'c': {
      if (!isB(param2)) return;

      // The compiler knows param2 is of type MyTypeB:

      const cf = this.resolver.resolveComponentFactory(MyClassBComponent);
      const component = this.wrap.createComponent(cf);

      component.instance.prop3 = param2.prop3;

      break;
    }
  }
}

请注意,此解决方案将生成其他代码,因为这些isAisB函数以及对它们的调用都将包含在生成的代码中。