diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dc1bab72c3..f507b0faad 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8869,6 +8869,7 @@ namespace ts { // An object type S is considered to be derived from an object type T if // S is a union type and every constituent of S is derived from T, // T is a union type and S is derived from at least one constituent of T, or + // S is a type variable with a base constraint that is derived from T, // T is one of the global types Object and Function and S is a subtype of T, or // T occurs directly or indirectly in an 'extends' clause of S. // Note that this check ignores type parameters and only considers the @@ -8876,6 +8877,7 @@ namespace ts { function isTypeDerivedFrom(source: Type, target: Type): boolean { return source.flags & TypeFlags.Union ? every((source).types, t => isTypeDerivedFrom(t, target)) : target.flags & TypeFlags.Union ? some((target).types, t => isTypeDerivedFrom(source, t)) : + source.flags & TypeFlags.TypeVariable ? isTypeDerivedFrom(getBaseConstraintOfType(source) || emptyObjectType, target) : target === globalObjectType || target === globalFunctionType ? isTypeSubtypeOf(source, target) : hasBaseType(source, getTargetType(target)); } diff --git a/tests/baselines/reference/narrowingConstrainedTypeVariable.js b/tests/baselines/reference/narrowingConstrainedTypeVariable.js new file mode 100644 index 0000000000..feb2018a7b --- /dev/null +++ b/tests/baselines/reference/narrowingConstrainedTypeVariable.js @@ -0,0 +1,79 @@ +//// [narrowingConstrainedTypeVariable.ts] +// Repro from #20138 + +class C { } + +function f1(v: T | string): void { + if (v instanceof C) { + const x: T = v; + } + else { + const s: string = v; + } +} + +class D { } + +function f2(v: T | U) { + if (v instanceof C) { + const x: T = v; + } + else { + const y: U = v; + } +} + +class E { x: string } + +function f3(v: T | { x: string }) { + if (v instanceof E) { + const x: T = v; + } + else { + const y: { x: string } = v; + } +} + + +//// [narrowingConstrainedTypeVariable.js] +"use strict"; +// Repro from #20138 +var C = /** @class */ (function () { + function C() { + } + return C; +}()); +function f1(v) { + if (v instanceof C) { + var x = v; + } + else { + var s = v; + } +} +var D = /** @class */ (function () { + function D() { + } + return D; +}()); +function f2(v) { + if (v instanceof C) { + var x = v; + } + else { + var y = v; + } +} +var E = /** @class */ (function () { + function E() { + } + return E; +}()); +function f3(v) { + if (v instanceof E) { + var x = v; + } + else { + var y = v; + } +} diff --git a/tests/baselines/reference/narrowingConstrainedTypeVariable.symbols b/tests/baselines/reference/narrowingConstrainedTypeVariable.symbols new file mode 100644 index 0000000000..e973eea702 --- /dev/null +++ b/tests/baselines/reference/narrowingConstrainedTypeVariable.symbols @@ -0,0 +1,88 @@ +=== tests/cases/conformance/types/typeRelationships/instanceOf/narrowingConstrainedTypeVariable.ts === +// Repro from #20138 + +class C { } +>C : Symbol(C, Decl(narrowingConstrainedTypeVariable.ts, 0, 0)) + +function f1(v: T | string): void { +>f1 : Symbol(f1, Decl(narrowingConstrainedTypeVariable.ts, 2, 11)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 4, 12)) +>C : Symbol(C, Decl(narrowingConstrainedTypeVariable.ts, 0, 0)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 4, 25)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 4, 12)) + + if (v instanceof C) { +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 4, 25)) +>C : Symbol(C, Decl(narrowingConstrainedTypeVariable.ts, 0, 0)) + + const x: T = v; +>x : Symbol(x, Decl(narrowingConstrainedTypeVariable.ts, 6, 13)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 4, 12)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 4, 25)) + } + else { + const s: string = v; +>s : Symbol(s, Decl(narrowingConstrainedTypeVariable.ts, 9, 13)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 4, 25)) + } +} + +class D { } +>D : Symbol(D, Decl(narrowingConstrainedTypeVariable.ts, 11, 1)) + +function f2(v: T | U) { +>f2 : Symbol(f2, Decl(narrowingConstrainedTypeVariable.ts, 13, 11)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 15, 12)) +>C : Symbol(C, Decl(narrowingConstrainedTypeVariable.ts, 0, 0)) +>U : Symbol(U, Decl(narrowingConstrainedTypeVariable.ts, 15, 24)) +>D : Symbol(D, Decl(narrowingConstrainedTypeVariable.ts, 11, 1)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 15, 38)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 15, 12)) +>U : Symbol(U, Decl(narrowingConstrainedTypeVariable.ts, 15, 24)) + + if (v instanceof C) { +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 15, 38)) +>C : Symbol(C, Decl(narrowingConstrainedTypeVariable.ts, 0, 0)) + + const x: T = v; +>x : Symbol(x, Decl(narrowingConstrainedTypeVariable.ts, 17, 13)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 15, 12)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 15, 38)) + } + else { + const y: U = v; +>y : Symbol(y, Decl(narrowingConstrainedTypeVariable.ts, 20, 13)) +>U : Symbol(U, Decl(narrowingConstrainedTypeVariable.ts, 15, 24)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 15, 38)) + } +} + +class E { x: string } +>E : Symbol(E, Decl(narrowingConstrainedTypeVariable.ts, 22, 1)) +>x : Symbol(E.x, Decl(narrowingConstrainedTypeVariable.ts, 24, 9)) + +function f3(v: T | { x: string }) { +>f3 : Symbol(f3, Decl(narrowingConstrainedTypeVariable.ts, 24, 21)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 26, 12)) +>E : Symbol(E, Decl(narrowingConstrainedTypeVariable.ts, 22, 1)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 26, 25)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 26, 12)) +>x : Symbol(x, Decl(narrowingConstrainedTypeVariable.ts, 26, 33)) + + if (v instanceof E) { +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 26, 25)) +>E : Symbol(E, Decl(narrowingConstrainedTypeVariable.ts, 22, 1)) + + const x: T = v; +>x : Symbol(x, Decl(narrowingConstrainedTypeVariable.ts, 28, 13)) +>T : Symbol(T, Decl(narrowingConstrainedTypeVariable.ts, 26, 12)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 26, 25)) + } + else { + const y: { x: string } = v; +>y : Symbol(y, Decl(narrowingConstrainedTypeVariable.ts, 31, 13)) +>x : Symbol(x, Decl(narrowingConstrainedTypeVariable.ts, 31, 18)) +>v : Symbol(v, Decl(narrowingConstrainedTypeVariable.ts, 26, 25)) + } +} + diff --git a/tests/baselines/reference/narrowingConstrainedTypeVariable.types b/tests/baselines/reference/narrowingConstrainedTypeVariable.types new file mode 100644 index 0000000000..aa8e30194f --- /dev/null +++ b/tests/baselines/reference/narrowingConstrainedTypeVariable.types @@ -0,0 +1,91 @@ +=== tests/cases/conformance/types/typeRelationships/instanceOf/narrowingConstrainedTypeVariable.ts === +// Repro from #20138 + +class C { } +>C : C + +function f1(v: T | string): void { +>f1 : (v: string | T) => void +>T : T +>C : C +>v : string | T +>T : T + + if (v instanceof C) { +>v instanceof C : boolean +>v : string | T +>C : typeof C + + const x: T = v; +>x : T +>T : T +>v : T + } + else { + const s: string = v; +>s : string +>v : string + } +} + +class D { } +>D : D + +function f2(v: T | U) { +>f2 : (v: T | U) => void +>T : T +>C : C +>U : U +>D : D +>v : T | U +>T : T +>U : U + + if (v instanceof C) { +>v instanceof C : boolean +>v : T | U +>C : typeof C + + const x: T = v; +>x : T +>T : T +>v : T + } + else { + const y: U = v; +>y : U +>U : U +>v : U + } +} + +class E { x: string } +>E : E +>x : string + +function f3(v: T | { x: string }) { +>f3 : (v: T | { x: string; }) => void +>T : T +>E : E +>v : T | { x: string; } +>T : T +>x : string + + if (v instanceof E) { +>v instanceof E : boolean +>v : T | { x: string; } +>E : typeof E + + const x: T = v; +>x : T +>T : T +>v : T + } + else { + const y: { x: string } = v; +>y : { x: string; } +>x : string +>v : { x: string; } + } +} + diff --git a/tests/cases/conformance/types/typeRelationships/instanceOf/narrowingConstrainedTypeVariable.ts b/tests/cases/conformance/types/typeRelationships/instanceOf/narrowingConstrainedTypeVariable.ts new file mode 100644 index 0000000000..13cc8da68c --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/instanceOf/narrowingConstrainedTypeVariable.ts @@ -0,0 +1,36 @@ +// @strict: true + +// Repro from #20138 + +class C { } + +function f1(v: T | string): void { + if (v instanceof C) { + const x: T = v; + } + else { + const s: string = v; + } +} + +class D { } + +function f2(v: T | U) { + if (v instanceof C) { + const x: T = v; + } + else { + const y: U = v; + } +} + +class E { x: string } + +function f3(v: T | { x: string }) { + if (v instanceof E) { + const x: T = v; + } + else { + const y: { x: string } = v; + } +}