I have several interfaces as below:
export interface INT1 {
prop1: string;
prop2: string;
}
export interface INT2 {
prop1: string;
prop3: string;
}
export interface INT3 {
prop1: string;
prop4: string;
}
Now I am trying to declare a BehaviorSubject
that returns one of the three interfaces above:
private _mySub$ = new BehaviorSubject<INT1 | INT2 | INT3>(undefined);
This doesn't work, because if I access the BehaviorSubject
value, I only find the shared properties (in this case only prop1
).
What am I doing wrong?
CodePudding user response:
There is nothing wrong with the code. Since _mySub$
could be a BehaviorSubject
of type INT1
, INT2
, or INT3
, static analysis can only guarantee that _mySub$
has property prop1
in all cases. It would be incorrect to infer the existence of other properties without narrowing the type.
If you know for a particular context that the BehaviorSubject
will always return exactly one of the interfaces, then you could use a more specific declaration such as:
private _mySub$ = new BehaviorSubject<INT1>({prop1: '1', prop2: '2'});
Type Guards
A common solution for handling broad type-definitions is to use TypeScript Type Guards.
Example: User-defined Type Guard for INT1
function instanceOfINT1(object: any): object is INT1 {
return 'prop2' in object;
}
Using the type guard, you could safely access prop2
when the BehaviorSubject
emitted an INT1
:
class MyClass {
private _mySub$ = new BehaviorSubject<INT1 | INT2 | INT3>({prop1: '1', prop2: '2'});
constructor() {
this._mySub$.subscribe( (val) => {
if (instanceOfINT1(val)) {
console.log(`val.prop2: ${val.prop2}`);
}
});
}
}
CodePudding user response:
Firstly you could declare a "base" interface with shared prop1 property.
export interface BASE {
prop1: string;
}
export interface INT2 extends BASE {
prop3: string;
}
export interface INT3 extends BASE {
prop4: string;
}
Secondly, you can use typescript assertion:
const int3: INT3 = obj as INT3;
https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions