Reputation: 8655
I am trying to provide an API with a function that accepts a parameter that can be of type A or B. I am using interfaces
to type the parameters. Those type do not share a single member. My attempts lead to compiler complaining about property x not present on type 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;
}
}
}
I am not sure this can be done with interface
s, I assume I might rather have to use type
, but don't know how.
Upvotes: 1
Views: 50
Reputation: 21161
I see 3 possible solutions:
param2
.I would go for Tagged Unions if it makes sense for you to combine param1
and param2
. Otherwise use User Defined Type Guards.
Too verbose in my opinion if you have to use it many times, but the most straight forward solution:
...
component.instance.prop1 = (param2 as MyTypeA).prop1;
component.instance.prop2 = (param2 as MyTypeA).prop2;
...
This solution will not generate additional code (the cast will be completelly removed from the generated code).
You can combine param1
and param2
and convert your custom types to 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;
}
}
}
This solution will not generate additional code (the interfaces, including the tagged param, will not be present in the generated code).
You can use User Defined Type Guards to narrow down the type of 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;
}
}
}
Note this solution will generate additional code, as those isA
and isB
functions, as well as the calls to them, will be included in the generated code.
Upvotes: 4