diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7aba7ccc1ff71..c6ccad6983e90 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7211,7 +7211,14 @@ namespace ts { return getUnionType(assignableConstituents); } } - + if (originalType.flags & TypeFlags.TypeParameter) { + const originalTypeParameter = originalType; + if (originalTypeParameter.constraint && isTypeAssignableTo(narrowedTypeCandidate, originalTypeParameter.constraint)) { + const narrowedTypeParameter = cloneTypeParameter(originalTypeParameter); + narrowedTypeParameter.constraint = narrowedTypeCandidate; + return narrowedTypeParameter; + } + } if (isTypeAssignableTo(narrowedTypeCandidate, originalType)) { // Narrow to the target type if it's assignable to the current type return narrowedTypeCandidate; diff --git a/tests/baselines/reference/narrowByTypeConstraint.js b/tests/baselines/reference/narrowByTypeConstraint.js new file mode 100644 index 0000000000000..9ca9cea1d6876 --- /dev/null +++ b/tests/baselines/reference/narrowByTypeConstraint.js @@ -0,0 +1,19 @@ +//// [narrowByTypeConstraint.ts] +interface Base { b } +interface Derived extends Base { d } +declare function isDerived(b: Base): b is Derived; +function f(x: T, y: T) { + return isDerived(x) && isDerived(y) && x.d === y.d; +} +function g(z: Base, ka: Base) { + return isDerived(z) && isDerived(ka) && z.d === ka.d; +} + + +//// [narrowByTypeConstraint.js] +function f(x, y) { + return isDerived(x) && isDerived(y) && x.d === y.d; +} +function g(z, ka) { + return isDerived(z) && isDerived(ka) && z.d === ka.d; +} diff --git a/tests/baselines/reference/narrowByTypeConstraint.symbols b/tests/baselines/reference/narrowByTypeConstraint.symbols new file mode 100644 index 0000000000000..f751a8cfa3739 --- /dev/null +++ b/tests/baselines/reference/narrowByTypeConstraint.symbols @@ -0,0 +1,58 @@ +=== tests/cases/compiler/narrowByTypeConstraint.ts === +interface Base { b } +>Base : Symbol(Base, Decl(narrowByTypeConstraint.ts, 0, 0)) +>b : Symbol(b, Decl(narrowByTypeConstraint.ts, 0, 16)) + +interface Derived extends Base { d } +>Derived : Symbol(Derived, Decl(narrowByTypeConstraint.ts, 0, 20)) +>Base : Symbol(Base, Decl(narrowByTypeConstraint.ts, 0, 0)) +>d : Symbol(d, Decl(narrowByTypeConstraint.ts, 1, 32)) + +declare function isDerived(b: Base): b is Derived; +>isDerived : Symbol(isDerived, Decl(narrowByTypeConstraint.ts, 1, 36)) +>b : Symbol(b, Decl(narrowByTypeConstraint.ts, 2, 27)) +>Base : Symbol(Base, Decl(narrowByTypeConstraint.ts, 0, 0)) +>b : Symbol(b, Decl(narrowByTypeConstraint.ts, 2, 27)) +>Derived : Symbol(Derived, Decl(narrowByTypeConstraint.ts, 0, 20)) + +function f(x: T, y: T) { +>f : Symbol(f, Decl(narrowByTypeConstraint.ts, 2, 50)) +>T : Symbol(T, Decl(narrowByTypeConstraint.ts, 3, 11)) +>Base : Symbol(Base, Decl(narrowByTypeConstraint.ts, 0, 0)) +>x : Symbol(x, Decl(narrowByTypeConstraint.ts, 3, 27)) +>T : Symbol(T, Decl(narrowByTypeConstraint.ts, 3, 11)) +>y : Symbol(y, Decl(narrowByTypeConstraint.ts, 3, 32)) +>T : Symbol(T, Decl(narrowByTypeConstraint.ts, 3, 11)) + + return isDerived(x) && isDerived(y) && x.d === y.d; +>isDerived : Symbol(isDerived, Decl(narrowByTypeConstraint.ts, 1, 36)) +>x : Symbol(x, Decl(narrowByTypeConstraint.ts, 3, 27)) +>isDerived : Symbol(isDerived, Decl(narrowByTypeConstraint.ts, 1, 36)) +>y : Symbol(y, Decl(narrowByTypeConstraint.ts, 3, 32)) +>x.d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +>x : Symbol(x, Decl(narrowByTypeConstraint.ts, 3, 27)) +>d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +>y.d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +>y : Symbol(y, Decl(narrowByTypeConstraint.ts, 3, 32)) +>d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +} +function g(z: Base, ka: Base) { +>g : Symbol(g, Decl(narrowByTypeConstraint.ts, 5, 1)) +>z : Symbol(z, Decl(narrowByTypeConstraint.ts, 6, 11)) +>Base : Symbol(Base, Decl(narrowByTypeConstraint.ts, 0, 0)) +>ka : Symbol(ka, Decl(narrowByTypeConstraint.ts, 6, 19)) +>Base : Symbol(Base, Decl(narrowByTypeConstraint.ts, 0, 0)) + + return isDerived(z) && isDerived(ka) && z.d === ka.d; +>isDerived : Symbol(isDerived, Decl(narrowByTypeConstraint.ts, 1, 36)) +>z : Symbol(z, Decl(narrowByTypeConstraint.ts, 6, 11)) +>isDerived : Symbol(isDerived, Decl(narrowByTypeConstraint.ts, 1, 36)) +>ka : Symbol(ka, Decl(narrowByTypeConstraint.ts, 6, 19)) +>z.d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +>z : Symbol(z, Decl(narrowByTypeConstraint.ts, 6, 11)) +>d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +>ka.d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +>ka : Symbol(ka, Decl(narrowByTypeConstraint.ts, 6, 19)) +>d : Symbol(Derived.d, Decl(narrowByTypeConstraint.ts, 1, 32)) +} + diff --git a/tests/baselines/reference/narrowByTypeConstraint.types b/tests/baselines/reference/narrowByTypeConstraint.types new file mode 100644 index 0000000000000..d0e05c25afd3b --- /dev/null +++ b/tests/baselines/reference/narrowByTypeConstraint.types @@ -0,0 +1,68 @@ +=== tests/cases/compiler/narrowByTypeConstraint.ts === +interface Base { b } +>Base : Base +>b : any + +interface Derived extends Base { d } +>Derived : Derived +>Base : Base +>d : any + +declare function isDerived(b: Base): b is Derived; +>isDerived : (b: Base) => b is Derived +>b : Base +>Base : Base +>b : any +>Derived : Derived + +function f(x: T, y: T) { +>f : (x: T, y: T) => boolean +>T : T +>Base : Base +>x : T +>T : T +>y : T +>T : T + + return isDerived(x) && isDerived(y) && x.d === y.d; +>isDerived(x) && isDerived(y) && x.d === y.d : boolean +>isDerived(x) && isDerived(y) : boolean +>isDerived(x) : boolean +>isDerived : (b: Base) => b is Derived +>x : T +>isDerived(y) : boolean +>isDerived : (b: Base) => b is Derived +>y : T +>x.d === y.d : boolean +>x.d : any +>x : T +>d : any +>y.d : any +>y : T +>d : any +} +function g(z: Base, ka: Base) { +>g : (z: Base, ka: Base) => boolean +>z : Base +>Base : Base +>ka : Base +>Base : Base + + return isDerived(z) && isDerived(ka) && z.d === ka.d; +>isDerived(z) && isDerived(ka) && z.d === ka.d : boolean +>isDerived(z) && isDerived(ka) : boolean +>isDerived(z) : boolean +>isDerived : (b: Base) => b is Derived +>z : Base +>isDerived(ka) : boolean +>isDerived : (b: Base) => b is Derived +>ka : Base +>z.d === ka.d : boolean +>z.d : any +>z : Derived +>d : any +>ka.d : any +>ka : Derived +>d : any +} + diff --git a/tests/cases/compiler/narrowByTypeConstraint.ts b/tests/cases/compiler/narrowByTypeConstraint.ts new file mode 100644 index 0000000000000..85de81c8015e1 --- /dev/null +++ b/tests/cases/compiler/narrowByTypeConstraint.ts @@ -0,0 +1,9 @@ +interface Base { b } +interface Derived extends Base { d } +declare function isDerived(b: Base): b is Derived; +function f(x: T, y: T) { + return isDerived(x) && isDerived(y) && x.d === y.d; +} +function g(z: Base, ka: Base) { + return isDerived(z) && isDerived(ka) && z.d === ka.d; +}