typescript - how do I properly write function signature to accept union-typed parameter? -
i trying provide api function accepts parameter can of type or b. using interfaces
type parameters. type not share single member. attempts lead compiler complaining 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 not sure can done interface
s, assume might rather have use type
, don't know how.
i see 3 possible solutions:
- cast each single usage of
param2
. - use tagged unions (aka discriminated unions or algebraic data types).
- use user defined type guards.
i go tagged unions if makes sense combine param1
, param2
. otherwise use user defined type guards.
👉 cast each single usage of param2:
too verbose in opinion if have use many times, straight forward solution:
... component.instance.prop1 = (param2 mytypea).prop1; component.instance.prop2 = (param2 mytypea).prop2; ...
this solution not generate additional code (the cast completelly removed generated code).
👉 use tagged unions (aka discriminated unions or algebraic data types):
you can combine param1
, param2
, convert custom types 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 knows param2 of type mytypea, because param1 // property 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': { // compiler knows param2 of type mytypeb, because param1 // property 'c'. const cf = this.resolver.resolvecomponentfactory(myclassbcomponent); const component = this.wrap.createcomponent(cf); component.instance.prop3 = param2.prop3; break; } } }
this solution not generate additional code (the interfaces, including tagged param, not present in generated code).
👉 use user defined type guards:
you can use user defined type guards narrow down type of param2
:
export interface mytypea { prop1: string; prop2: boolean; } export interface mytypeb { prop3: number; prop4: string; } function isa(arg: any): arg mytypea { return arg.hasownproperty('prop1'); } function isb(arg: any): arg mytypeb { return arg.hasownproperty('prop3'); } dosomething(param1: string, param2: mytypea | mytypeb) { switch(param1) { case 'a': case 'b': { if (!isa(param2)) return; // compiler knows param2 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; // compiler knows param2 of type mytypeb: const cf = this.resolver.resolvecomponentfactory(myclassbcomponent); const component = this.wrap.createcomponent(cf); component.instance.prop3 = param2.prop3; break; } } }
note solution generate additional code, isa
, isb
functions, calls them, included in generated code.
Comments
Post a Comment