From 1e96c5730206f868408e31ce8152e763157dfbed Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 25 Jul 2022 14:59:52 -0700 Subject: [PATCH 1/2] Favor asserted type in type predicate narrowing --- src/compiler/checker.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7768b78ef3ecd..a24a8518618a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25428,8 +25428,12 @@ namespace ts { const discriminant = keyPropertyName && getTypeOfPropertyOfType(c, keyPropertyName); const matching = discriminant && getConstituentTypeForKeyType(type as UnionType, discriminant); // For each constituent t in the current type, if t and and c are directly related, pick the most - // specific of the two. - const directlyRelated = mapType(matching || type, t => isRelated(t, c) ? t : isRelated(c, t) ? c : neverType); + // specific of the two. When t and c are related in both directions, we prefer c for type predicates + // because that is the asserted type, but t for `instanceof` because generics aren't reflected in + // prototype object types. + const directlyRelated = mapType(matching || type, checkDerived ? + t => isTypeDerivedFrom(t, c) ? t : isTypeDerivedFrom(c, t) ? c : neverType : + t => isTypeSubtypeOf(c, t) ? c : isTypeSubtypeOf(t, c) ? t : neverType); // If no constituents are directly related, create intersections for any generic constituents that // are related by constraint. return directlyRelated.flags & TypeFlags.Never ? From a9484d3d4d9b3c8c26f7038e68b8b078972afe91 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 25 Jul 2022 15:00:25 -0700 Subject: [PATCH 2/2] Accept new baselines --- .../reference/controlFlowBinaryOrExpression.symbols | 8 ++++---- .../reference/controlFlowBinaryOrExpression.types | 8 ++++---- .../instanceofWithStructurallyIdenticalTypes.symbols | 4 ++-- .../instanceofWithStructurallyIdenticalTypes.types | 2 +- .../reference/typePredicateStructuralMatch.symbols | 4 ++-- tests/baselines/reference/unionWithIndexSignature.types | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols index 5251973005fcd..3c2bfaa2f7cf0 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols @@ -64,9 +64,9 @@ if (isNodeList(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) } if (isHTMLCollection(sourceObj)) { @@ -74,9 +74,9 @@ if (isHTMLCollection(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) } if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index 9c188295a82f0..23e6034ce1cf0 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -69,18 +69,18 @@ if (isNodeList(sourceObj)) { sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection +>sourceObj : NodeList >length : number } if (isHTMLCollection(sourceObj)) { >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection ->sourceObj : EventTargetLike +>sourceObj : NodeList | { a: string; } sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection +>sourceObj : HTMLCollection >length : number } @@ -88,7 +88,7 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { >isNodeList(sourceObj) || isHTMLCollection(sourceObj) : boolean >isNodeList(sourceObj) : boolean >isNodeList : (sourceObj: any) => sourceObj is NodeList ->sourceObj : EventTargetLike +>sourceObj : HTMLCollection | { a: string; } >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection >sourceObj : { a: string; } diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols index 601699b93f716..868d1c1fd784e 100644 --- a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols @@ -95,9 +95,9 @@ function foo2(x: C1 | C2 | C3): string { >x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) return x.item; ->x.item : Symbol(item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10), Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) +>x.item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) >x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) ->item : Symbol(item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10), Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) +>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) } else if (isC2(x)) { >isC2 : Symbol(isC2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 66)) diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types index 113c994c6e619..6aeba6f1b733a 100644 --- a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types @@ -85,7 +85,7 @@ function foo2(x: C1 | C2 | C3): string { return x.item; >x.item : string ->x : C1 | C3 +>x : C1 >item : string } else if (isC2(x)) { diff --git a/tests/baselines/reference/typePredicateStructuralMatch.symbols b/tests/baselines/reference/typePredicateStructuralMatch.symbols index 5e4826ebbf2e8..75fea1027d2c3 100644 --- a/tests/baselines/reference/typePredicateStructuralMatch.symbols +++ b/tests/baselines/reference/typePredicateStructuralMatch.symbols @@ -51,9 +51,9 @@ function getResults1(value: Results | { data: Results }): Results { return isResponseInData(value) ? value.data : value; >isResponseInData : Symbol(isResponseInData, Decl(typePredicateStructuralMatch.ts, 9, 24)) >value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21)) ->value.data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 15, 39)) +>value.data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 11, 63)) >value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21)) ->data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 15, 39)) +>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 11, 63)) >value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21)) } diff --git a/tests/baselines/reference/unionWithIndexSignature.types b/tests/baselines/reference/unionWithIndexSignature.types index 7aa5877e37a25..2bc7d0fbe5c9e 100644 --- a/tests/baselines/reference/unionWithIndexSignature.types +++ b/tests/baselines/reference/unionWithIndexSignature.types @@ -55,7 +55,7 @@ export function flatten(arr: T) { arr[1]; >arr[1] : number ->arr : TypedArray +>arr : Int32Array | Uint8Array >1 : 1 } }