There are 3 potential solutions:
- Ensure each individual use of
param2
is casted.
- Utilize Tagged Unions (also known as discriminated unions or algebraic data types).
- Implement User Defined Type Guards.
If combining param1
and param2
makes sense to you, Opt for Tagged Unions. Otherwise, go with User Defined Type Guards.
👉 Cast each single usage of param2:
This approach might become too long-winded if used extensively, but it's the most direct method:
...
component.instance.prop1 = (param2 as MyTypeA).prop1;
component.instance.prop2 = (param2 as MyTypeA).prop2;
...
Employing this solution won't introduce extra code; the cast will be entirely removed from the generated code.
👉 Utilize Tagged Unions (also known as discriminated unions or algebraic data types):
You can merge param1
and param2
, converting 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': {
// Compiler identifies param2 as type MyTypeA due to its param1 property being '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': {
// Compiler identifies param2 as 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 method won't add additional code when generated; the interfaces, including the tagged param, won't appear in the created code.
👉 Implement User Defined Type Guards:
You can narrow down the type of param2
using User Defined Type Guards:
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;
// Compiler recognizes param2 as 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;
// Compiler recognizes param2 as type MyTypeB:
const cf = this.resolver.resolveComponentFactory(MyClassBComponent);
const component = this.wrap.createComponent(cf);
component.instance.prop3 = param2.prop3;
break;
}
}
}
Note that this method will result in additional code generation - the functions isA and isB, along with their calls, will be included in the output code.