TypeScript Version: 3.4.0-dev.201xxxxx
Code
declare let x: Partial<HTMLElement>;
function f<T>(y: T) {
x = y;
}
f({ innerHtml: Symbol() }); // this is blatantly incompatible with `HTMLElement`
if (x.innerHTML) {
x.innerHTML.toLowerCase(); // We just set it to a `symbol` and not a `string`, this will error at runtime
}
Expected behavior:
An error on x = y.
Actual behavior:
No error.
Playground Link
The root cause is this relationship in structuredTypeRelatedTo added way back in this:
if (relation !== subtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) {
return Ternary.True;
}
This is unsound when source is the empty type resulting from the constraint of an unconstrained generic type (which, if changed to unknown, catches this issue!). It's unsound when it comes from a constraint at all, actually. Fixing this will break react-redux, whose recursive Shared type (which we have in our test suite) actually only checks because of this unsoundness.
TypeScript Version: 3.4.0-dev.201xxxxx
Code
Expected behavior:
An error on
x = y.Actual behavior:
No error.
Playground Link
The root cause is this relationship in
structuredTypeRelatedToadded way back in this:This is unsound when
sourceis the empty type resulting from the constraint of an unconstrained generic type (which, if changed tounknown, catches this issue!). It's unsound when it comes from a constraint at all, actually. Fixing this will breakreact-redux, whose recursiveSharedtype (which we have in our test suite) actually only checks because of this unsoundness.