Description
🔎 Search Terms
"type relationships" "type compatibility" instances generic interface "type parameter" measuring variance
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about type compatibility for generics
⏯ Playground Link
💻 Code
interface Thing1 {
value: string;
}
interface Thing2 {
value: string;
extra: string;
}
interface Wrapper<Thing extends {value: unknown}> {
value: Thing['value'];
}
declare const thing1: Wrapper<Thing1>;
const thing2: Wrapper<Thing2> = thing1;
🙁 Actual behavior
Type 'Wrapper' is not assignable to type 'Wrapper'.
Property 'extra' is missing in type 'Thing1' but required in type 'Thing2'.
extra
is not used at all in the definition of Wrapper
, but somehow it affects the compatibility between different instantiations of Wrapper
.
🙂 Expected behavior
No error, Wrapper<Thing1>
and Wrapper<Thing2>
are structurally identical and should ideally be the same type internally.
Additional information about the issue
The error does not happen if Wrapper
is defined as an intersection with a dummy {}
type:
type GoodWrapper<Thing extends {value: unknown}> = {} & {
value: Thing['value'];
};
declare const goodThing1: GoodWrapper<Thing1>;
const goodThing2: GoodWrapper<Thing2> = goodThing1; // ok
The only similar issue that I managed to find is Generic interfaces flagged incompatible, as if nominal typing [fixed], which does not produce an unexpected error any more, but also is marked "Working as intended". The explanation is that it's a result of an optimization, and "This optimization depends on types being sensible".
If this is indeed a result of the optimization which is suppressed by using an intersection type, the question is how reliable that suppression is.
Also, I don't see anything not sensible in the example code - in the real code, types Thing1
and Thing2
are defined in the user code, and the Wrapper
is defined in the library that only cares about one particular aspect of things.