Let's assume we have two interfaces:
interface A {
name: string;
_id?: string;
}
interface B {
name: string;
_id: string;
}
When assigning them to each other, typescript behaves like I would expect it:
const a: A = { name: 'a' };
const b: B = { name: 'b', _id: '1' };
const c: B = a; // Type A is not assignable to type B
const d: A = b;
const getA = (): A => {
return b;
};
In line 3, I'm getting the following error:
TS2322: Type A is not assignable to type B
Types of property _id are incompatible.
Type string | undefined is not assignable to type string
Type undefined is not assignable to type string
That make sense, because the _id
of a
could be undefined.
When I use A with an BehaviourSubject likt this, I'm also getting an error:
const behA = new BehaviorSubject<A>(a);
const behB = new BehaviorSubject<B>(b);
const getBehA = (): BehaviorSubject<A> => {
return behB;
}
S2322: Type BehaviorSubject<B> is not assignable to type BehaviorSubject<A>
Types of property observers are incompatible.
Type Observer<B>[] is not assignable to type Observer<A>[]
Type Observer<B> is not assignable to type Observer<A>
Type A is not assignable to type B
Types of property _id are incompatible.
Type string | undefined is not assignable to type string
Type undefined is not assignable to type string
For me it is not really clear why. If I try the same with a simple container interface, it works fine:
interface Container<T> {
value: T;
}
const cB: Container<B> = {
value: {
name: 'name',
_id: '1234',
},
};
const getCA = (): Container<A> => {
return cB;
}
As the error says:
✘ [ERROR] TS2322: Type 'BehaviorSubject<B>' is not assignable to type 'BehaviorSubject<A>'.
Types of property 'observers' are incompatible.
Type 'Observer<B>' is not assignable to type 'Observer<A>'.
Type 'A' is not assignable to type 'B'.
Types of property '_id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'. [plugin angular-compiler]
src/main.ts:79:2:
79 │ return behB;
╵ ~~~~~~
The behavior subject will map perfectly but it extends Subject
.
export declare class BehaviorSubject<T> extends Subject<T> {
private _value;
constructor(_value: T);
get value(): T;
getValue(): T;
next(value: T): void;
}
but it was due to observers
which is an array of Observer<T>
(extends Subject<T>
), so here the type checking is unable to check the individual properties on the properties that were inherited from Subject<T>
for some reason (check next point):
export declare class Subject<T> extends Observable<T> implements SubscriptionLike {
closed: boolean;
private currentObservers;
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
observers: Observer<T>;
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
isStopped: boolean;
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
hasError: boolean;
...
Inside observers
the next
property is what gives the error:
export interface Observer<T> {
next: (value: T) => void;
...
So, the type checking does not work when it is at the callback level, hence you might face this problem. I don't know the exact reason, but this is my analysis.