The following code snippet doesn't compile, and I don't understand why. Sorry that it's a little verbose, this is the smallest MWE I could find.
interface Num {
num: number;
}
class Base {
num?: number;
base(): this {
return this;
}
addNum(): this & Num {
this.num = 1;
return this as this & Num;
}
}
function unwrapNum<B extends Base>(base: B): B & Num {
return base.addNum().base();
}
it fails with error:
Type 'B' is not assignable to type 'B & Num'.
Type 'Base' is not assignable to type 'B & Num'.
Type 'Base' is not assignable to type 'B'.
'Base' is assignable to the constraint of type 'B', but 'B' could be instantiated with a different subtype of constraint 'Base'.
Type 'B' is not assignable to type 'Num'.
Type 'Base' is not assignable to type 'Num'.
Types of property 'num' are incompatible.
Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
My understanding is the input type is B after calling addNum it should turn into B & Num which it seems to if I break out that assignment. However, when I call base() on that, the result doesn't have type B & Num as I expect / want, but instead just B, which implies that something isn't working as expected. My hunch is that this has to do with the polymorphic this, e.g. (B & Num).base() does not have type B & Num.
However, if I remove the generics with something like:
const base = new Base();
const numed: Base & Num = base.addNum();
const numed2: Base & Num = numed.base();
that works just fine, so it feels like it's a combination of the generic Base, polymorphic this, and intersection type.
Stangely, this does compile if wrapped in another generic:
function unwrapNum<B extends Base>(base: B): B & Num {
function helper<BN extends B & Num>(b: BN): BN {
return b.base();
}
return helper(base.addNum());
}
Thanks Aleksey L. for the smaller MWQ