From 02b861e1018434cc2f735547c8014a8db9e7340f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Apr 2019 17:00:30 -0700 Subject: [PATCH 1/8] Use getBaseSignature when relating instantiations of the same signature --- src/compiler/checker.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 308b95c218611..1f642c82d9847 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11927,7 +11927,7 @@ namespace ts { // with respect to T. const sourceSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(sourceType)); const targetSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(targetType)); - const callbacks = sourceSig && targetSig && !signatureHasTypePredicate(sourceSig) && !signatureHasTypePredicate(targetSig) && + const callbacks = sourceSig && targetSig && (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable); const related = callbacks ? // TODO: GH#18217 It will work if they're both `undefined`, but not if only one is @@ -11958,7 +11958,8 @@ namespace ts { if (targetTypePredicate) { const sourceTypePredicate = getTypePredicateOfSignature(source); if (sourceTypePredicate) { - result &= compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes); + result &= callbackCheck === CallbackCheck.Bivariant && compareTypePredicateRelatedTo(targetTypePredicate, sourceTypePredicate, /*reportErrors*/ false, undefined, compareTypes) || + compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes); } else if (isIdentifierTypePredicate(targetTypePredicate)) { if (reportErrors) { @@ -13394,10 +13395,11 @@ namespace ts { if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) { // We have instantiations of the same anonymous type (which typically will be the type of a // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We erase type parameters - // as they are known to always be the same. + // of the much more expensive N * M comparison matrix we explore below. We instantiate type + // parameters to their constraints because, whereas the type parameters are known to be the same, + // the constraints might differ if they reference outer type parameters. for (let i = 0; i < targetSignatures.length; i++) { - const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors); + const related = signatureRelatedTo(getBaseSignature(sourceSignatures[i]), getBaseSignature(targetSignatures[i]), /*erase*/ false, reportErrors); if (!related) { return Ternary.False; } From bd806419cdb8bc54a3ce3e201e492eb4373bdbdd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Apr 2019 17:05:05 -0700 Subject: [PATCH 2/8] Accept new baselines --- tests/baselines/reference/complexRecursiveCollections.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/complexRecursiveCollections.types b/tests/baselines/reference/complexRecursiveCollections.types index feaff62048628..a9fc13e7d1dc1 100644 --- a/tests/baselines/reference/complexRecursiveCollections.types +++ b/tests/baselines/reference/complexRecursiveCollections.types @@ -1137,7 +1137,7 @@ declare module Immutable { >Seq : typeof Seq function isSeq(maybeSeq: any): maybeSeq is Seq.Indexed | Seq.Keyed; ->isSeq : (maybeSeq: any) => maybeSeq is Indexed | Keyed +>isSeq : (maybeSeq: any) => maybeSeq is Keyed | Indexed >maybeSeq : any >Seq : any >Seq : any From fda21f1560c40209a82f2205c78b6e6d9a82044b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Apr 2019 17:20:21 -0700 Subject: [PATCH 3/8] Add tests --- .../types/keyof/keyofAndIndexedAccess2.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts index 26daf35ee50c4..547ef9146adaa 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts @@ -117,3 +117,18 @@ type StrictExtract = T extends U ? U extends T ? T : never : never; type StrictExclude = T extends StrictExtract ? never : T; type A = { [Q in { [P in keyof T]: P; }[keyof T]]: T[Q]; }; type B = A<{ [Q in keyof T]: StrictExclude, {}>; }>; + +// Repro from 31006 + +type Demo = (key: K, val: T[K]) => void; + +declare let da: Demo<{ a: number }>; +declare let db: Demo<{ b: string }>; +declare let dc: Demo<{ a: number, b: string }>; + +da = db; // Error +da = dc; +db = da; // Error +db = dc; +dc = da; // Error +dc = db; // Error From 34feb07d6ff356c6f0ce1c7994b78703d5ca7211 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Apr 2019 17:20:30 -0700 Subject: [PATCH 4/8] Accept new baselines --- .../keyofAndIndexedAccess2.errors.txt | 41 +++++++++++++- .../reference/keyofAndIndexedAccess2.js | 21 ++++++++ .../reference/keyofAndIndexedAccess2.symbols | 53 +++++++++++++++++++ .../reference/keyofAndIndexedAccess2.types | 50 +++++++++++++++++ 4 files changed, 164 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt b/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt index aef57c129a33d..279797244f205 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt @@ -25,9 +25,17 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(68,3): error TS232 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS2322: Type '123' is not assignable to type 'Type[K]'. Type '123' is not assignable to type '123 & "some string"'. Type '123' is not assignable to type '"some string"'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(126,1): error TS2322: Type 'Demo<{ b: string; }>' is not assignable to type 'Demo<{ a: number; }>'. + Property 'a' is missing in type '{ b: string; }' but required in type '{ a: number; }'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(128,1): error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ b: string; }>'. + Property 'b' is missing in type '{ a: number; }' but required in type '{ b: string; }'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(130,1): error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ a: number; b: string; }>'. + Property 'b' is missing in type '{ a: number; }' but required in type '{ a: number; b: string; }'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(131,1): error TS2322: Type 'Demo<{ b: string; }>' is not assignable to type 'Demo<{ a: number; b: string; }>'. + Property 'a' is missing in type '{ b: string; }' but required in type '{ a: number; b: string; }'. -==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts (23 errors) ==== +==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts (27 errors) ==== function f1(obj: { a: number, b: 0 | 1, c: string }, k0: 'a', k1: 'a' | 'b', k2: 'a' | 'b' | 'c') { obj[k0] = 1; obj[k0] = 2; @@ -194,4 +202,35 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS23 type StrictExclude = T extends StrictExtract ? never : T; type A = { [Q in { [P in keyof T]: P; }[keyof T]]: T[Q]; }; type B = A<{ [Q in keyof T]: StrictExclude, {}>; }>; + + // Repro from 31006 + + type Demo = (key: K, val: T[K]) => void; + + declare let da: Demo<{ a: number }>; + declare let db: Demo<{ b: string }>; + declare let dc: Demo<{ a: number, b: string }>; + + da = db; // Error + ~~ +!!! error TS2322: Type 'Demo<{ b: string; }>' is not assignable to type 'Demo<{ a: number; }>'. +!!! error TS2322: Property 'a' is missing in type '{ b: string; }' but required in type '{ a: number; }'. +!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:122:24: 'a' is declared here. + da = dc; + db = da; // Error + ~~ +!!! error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ b: string; }>'. +!!! error TS2322: Property 'b' is missing in type '{ a: number; }' but required in type '{ b: string; }'. +!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:123:24: 'b' is declared here. + db = dc; + dc = da; // Error + ~~ +!!! error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ a: number; b: string; }>'. +!!! error TS2322: Property 'b' is missing in type '{ a: number; }' but required in type '{ a: number; b: string; }'. +!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:124:35: 'b' is declared here. + dc = db; // Error + ~~ +!!! error TS2322: Type 'Demo<{ b: string; }>' is not assignable to type 'Demo<{ a: number; b: string; }>'. +!!! error TS2322: Property 'a' is missing in type '{ b: string; }' but required in type '{ a: number; b: string; }'. +!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:124:24: 'a' is declared here. \ No newline at end of file diff --git a/tests/baselines/reference/keyofAndIndexedAccess2.js b/tests/baselines/reference/keyofAndIndexedAccess2.js index 1788c40563c1b..4f003a1059431 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess2.js +++ b/tests/baselines/reference/keyofAndIndexedAccess2.js @@ -115,6 +115,21 @@ type StrictExtract = T extends U ? U extends T ? T : never : never; type StrictExclude = T extends StrictExtract ? never : T; type A = { [Q in { [P in keyof T]: P; }[keyof T]]: T[Q]; }; type B = A<{ [Q in keyof T]: StrictExclude, {}>; }>; + +// Repro from 31006 + +type Demo = (key: K, val: T[K]) => void; + +declare let da: Demo<{ a: number }>; +declare let db: Demo<{ b: string }>; +declare let dc: Demo<{ a: number, b: string }>; + +da = db; // Error +da = dc; +db = da; // Error +db = dc; +dc = da; // Error +dc = db; // Error //// [keyofAndIndexedAccess2.js] @@ -190,3 +205,9 @@ export function getEntity(id, state) { function get123() { return 123; // Error } +da = db; // Error +da = dc; +db = da; // Error +db = dc; +dc = da; // Error +dc = db; // Error diff --git a/tests/baselines/reference/keyofAndIndexedAccess2.symbols b/tests/baselines/reference/keyofAndIndexedAccess2.symbols index abbe38fd5c9c8..19fe325246296 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess2.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess2.symbols @@ -434,3 +434,56 @@ type B = A<{ [Q in keyof T]: StrictExclude, {}>; }>; >Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 115, 20)) >V : Symbol(V, Decl(keyofAndIndexedAccess2.ts, 115, 9)) +// Repro from 31006 + +type Demo = (key: K, val: T[K]) => void; +>Demo : Symbol(Demo, Decl(keyofAndIndexedAccess2.ts, 115, 69)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 119, 10)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 119, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 119, 10)) +>key : Symbol(key, Decl(keyofAndIndexedAccess2.ts, 119, 35)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 119, 16)) +>val : Symbol(val, Decl(keyofAndIndexedAccess2.ts, 119, 42)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 119, 10)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 119, 16)) + +declare let da: Demo<{ a: number }>; +>da : Symbol(da, Decl(keyofAndIndexedAccess2.ts, 121, 11)) +>Demo : Symbol(Demo, Decl(keyofAndIndexedAccess2.ts, 115, 69)) +>a : Symbol(a, Decl(keyofAndIndexedAccess2.ts, 121, 22)) + +declare let db: Demo<{ b: string }>; +>db : Symbol(db, Decl(keyofAndIndexedAccess2.ts, 122, 11)) +>Demo : Symbol(Demo, Decl(keyofAndIndexedAccess2.ts, 115, 69)) +>b : Symbol(b, Decl(keyofAndIndexedAccess2.ts, 122, 22)) + +declare let dc: Demo<{ a: number, b: string }>; +>dc : Symbol(dc, Decl(keyofAndIndexedAccess2.ts, 123, 11)) +>Demo : Symbol(Demo, Decl(keyofAndIndexedAccess2.ts, 115, 69)) +>a : Symbol(a, Decl(keyofAndIndexedAccess2.ts, 123, 22)) +>b : Symbol(b, Decl(keyofAndIndexedAccess2.ts, 123, 33)) + +da = db; // Error +>da : Symbol(da, Decl(keyofAndIndexedAccess2.ts, 121, 11)) +>db : Symbol(db, Decl(keyofAndIndexedAccess2.ts, 122, 11)) + +da = dc; +>da : Symbol(da, Decl(keyofAndIndexedAccess2.ts, 121, 11)) +>dc : Symbol(dc, Decl(keyofAndIndexedAccess2.ts, 123, 11)) + +db = da; // Error +>db : Symbol(db, Decl(keyofAndIndexedAccess2.ts, 122, 11)) +>da : Symbol(da, Decl(keyofAndIndexedAccess2.ts, 121, 11)) + +db = dc; +>db : Symbol(db, Decl(keyofAndIndexedAccess2.ts, 122, 11)) +>dc : Symbol(dc, Decl(keyofAndIndexedAccess2.ts, 123, 11)) + +dc = da; // Error +>dc : Symbol(dc, Decl(keyofAndIndexedAccess2.ts, 123, 11)) +>da : Symbol(da, Decl(keyofAndIndexedAccess2.ts, 121, 11)) + +dc = db; // Error +>dc : Symbol(dc, Decl(keyofAndIndexedAccess2.ts, 123, 11)) +>db : Symbol(db, Decl(keyofAndIndexedAccess2.ts, 122, 11)) + diff --git a/tests/baselines/reference/keyofAndIndexedAccess2.types b/tests/baselines/reference/keyofAndIndexedAccess2.types index 4eccf23a80430..f8ede140f4a75 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess2.types +++ b/tests/baselines/reference/keyofAndIndexedAccess2.types @@ -437,3 +437,53 @@ type A = { [Q in { [P in keyof T]: P; }[keyof T]]: T[Q]; }; type B = A<{ [Q in keyof T]: StrictExclude, {}>; }>; >B : A<{ [Q in keyof T]: StrictExclude, {}>; }>, {}>; }>, {}>; }>, {}>; }>, {}>; }>, {}>; }>, {}>; }>, {}>; }>, {}>; }>, {}>; }>, {}>; }> +// Repro from 31006 + +type Demo = (key: K, val: T[K]) => void; +>Demo : Demo +>key : K +>val : T[K] + +declare let da: Demo<{ a: number }>; +>da : Demo<{ a: number; }> +>a : number + +declare let db: Demo<{ b: string }>; +>db : Demo<{ b: string; }> +>b : string + +declare let dc: Demo<{ a: number, b: string }>; +>dc : Demo<{ a: number; b: string; }> +>a : number +>b : string + +da = db; // Error +>da = db : Demo<{ b: string; }> +>da : Demo<{ a: number; }> +>db : Demo<{ b: string; }> + +da = dc; +>da = dc : Demo<{ a: number; b: string; }> +>da : Demo<{ a: number; }> +>dc : Demo<{ a: number; b: string; }> + +db = da; // Error +>db = da : Demo<{ a: number; }> +>db : Demo<{ b: string; }> +>da : Demo<{ a: number; }> + +db = dc; +>db = dc : Demo<{ a: number; b: string; }> +>db : Demo<{ b: string; }> +>dc : Demo<{ a: number; b: string; }> + +dc = da; // Error +>dc = da : Demo<{ a: number; }> +>dc : Demo<{ a: number; b: string; }> +>da : Demo<{ a: number; }> + +dc = db; // Error +>dc = db : Demo<{ b: string; }> +>dc : Demo<{ a: number; b: string; }> +>db : Demo<{ b: string; }> + From 78f54c5bb60a2495ca7402180a3310b569b8fd23 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Apr 2019 17:42:47 -0700 Subject: [PATCH 5/8] Fix lint error --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f642c82d9847..75b2934348af8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11958,7 +11958,7 @@ namespace ts { if (targetTypePredicate) { const sourceTypePredicate = getTypePredicateOfSignature(source); if (sourceTypePredicate) { - result &= callbackCheck === CallbackCheck.Bivariant && compareTypePredicateRelatedTo(targetTypePredicate, sourceTypePredicate, /*reportErrors*/ false, undefined, compareTypes) || + result &= callbackCheck === CallbackCheck.Bivariant && compareTypePredicateRelatedTo(targetTypePredicate, sourceTypePredicate, /*reportErrors*/ false, /*errorReporter*/ undefined, compareTypes) || compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes); } else if (isIdentifierTypePredicate(targetTypePredicate)) { From df50477d31490bf3d933de2f35e282883d322d07 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 20 Apr 2019 17:27:15 -0700 Subject: [PATCH 6/8] Improve checking of instantiations of same signature --- src/compiler/checker.ts | 44 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 75b2934348af8..635cb2ee01947 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8550,11 +8550,16 @@ namespace ts { isInJSFile(signature.declaration)); } + function getErasedConstraint(type: Type, typeParameters: readonly TypeParameter[]): Type | undefined { + const constraint = getConstraintOfType(type); + return constraint && contains(typeParameters, constraint) ? getErasedConstraint(constraint, typeParameters) : constraint; + } + function getBaseSignature(signature: Signature) { const typeParameters = signature.typeParameters; if (typeParameters) { const typeEraser = createTypeEraser(typeParameters); - const baseConstraints = map(typeParameters, tp => instantiateType(getBaseConstraintOfType(tp), typeEraser) || unknownType); + const baseConstraints = map(typeParameters, tp => instantiateType(getErasedConstraint(tp, typeParameters), typeEraser) || unknownType); return instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true); } return signature; @@ -13392,35 +13397,33 @@ namespace ts { let result = Ternary.True; const saveErrorInfo = errorInfo; - if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) { - // We have instantiations of the same anonymous type (which typically will be the type of a - // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We instantiate type - // parameters to their constraints because, whereas the type parameters are known to be the same, - // the constraints might differ if they reference outer type parameters. + const sameSignatureInstantiations = getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol; + if (sameSignatureInstantiations || sourceSignatures.length === 1 && targetSignatures.length === 1) { + // We have instantiations of the same anonymous type (which typically will be the type of a method) + // or we have non-overloaded signatures. Simply do a pairwise comparison of the signatures in the + // two signature lists instead of the much more expensive N * M comparison matrix we explore below. + const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks; for (let i = 0; i < targetSignatures.length; i++) { - const related = signatureRelatedTo(getBaseSignature(sourceSignatures[i]), getBaseSignature(targetSignatures[i]), /*erase*/ false, reportErrors); + const s = sourceSignatures[i]; + const t = targetSignatures[i]; + // We erase type parameters for the comparable relation or when strict checks are disabled. + // Otherwise, when we have instantiations of the same anonymous type, we instantiate target + // type parameters to their constraints because the type parameters are known to be the same. + const effectiveSource = eraseGenerics ? getErasedSignature(s) : s; + const effectiveTarget = eraseGenerics ? getErasedSignature(t) : sameSignatureInstantiations ? getBaseSignature(t) : t; + const related = signatureRelatedTo(effectiveSource, effectiveTarget, reportErrors); if (!related) { return Ternary.False; } result &= related; } } - else if (sourceSignatures.length === 1 && targetSignatures.length === 1) { - // For simple functions (functions with a single signature) we only erase type parameters for - // the comparable relation. Otherwise, if the source signature is generic, we instantiate it - // in the context of the target signature before checking the relationship. Ideally we'd do - // this regardless of the number of signatures, but the potential costs are prohibitive due - // to the quadratic nature of the logic below. - const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks; - result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors); - } else { outer: for (const t of targetSignatures) { // Only elaborate errors from the first failure let shouldElaborateErrors = reportErrors; for (const s of sourceSignatures) { - const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors); + const related = signatureRelatedTo(getErasedSignature(s), getErasedSignature(t), shouldElaborateErrors); if (related) { result &= related; errorInfo = saveErrorInfo; @@ -13443,9 +13446,8 @@ namespace ts { /** * See signatureAssignableTo, compareSignaturesIdentical */ - function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean): Ternary { - return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target, - CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo); + function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary { + return compareSignaturesRelated(source, target, CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo); } function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary { From 39c263b2f5319382f89833db9aa550782a2b5b44 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 21 Apr 2019 11:25:51 -0700 Subject: [PATCH 7/8] Fix bivariant checking of signatures returning type predicates --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 635cb2ee01947..a3b7491ad2278 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11960,8 +11960,8 @@ namespace ts { // The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions const targetTypePredicate = getTypePredicateOfSignature(target); - if (targetTypePredicate) { - const sourceTypePredicate = getTypePredicateOfSignature(source); + const sourceTypePredicate = getTypePredicateOfSignature(source); + if (targetTypePredicate && (sourceTypePredicate || callbackCheck !== CallbackCheck.Bivariant)) { if (sourceTypePredicate) { result &= callbackCheck === CallbackCheck.Bivariant && compareTypePredicateRelatedTo(targetTypePredicate, sourceTypePredicate, /*reportErrors*/ false, /*errorReporter*/ undefined, compareTypes) || compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes); From e262f49417807dac9e44d09f9ed07bdce07a9052 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 5 Mar 2020 17:37:50 -0800 Subject: [PATCH 8/8] Accept new baselines --- .../keyofAndIndexedAccess2.errors.txt | 22 ++++- .../reference/keyofAndIndexedAccess2.symbols | 88 +++++++++---------- 2 files changed, 62 insertions(+), 48 deletions(-) diff --git a/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt b/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt index 996ddba323f57..5e49aa37b6c82 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccess2.errors.txt @@ -26,6 +26,24 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(67,3): error TS232 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(68,3): error TS2322: Type '123' is not assignable to type 'T[K]'. tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS2322: Type '123' is not assignable to type 'Type[K]'. Type '123' is not assignable to type 'never'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(126,1): error TS2322: Type 'Demo<{ b: string; }>' is not assignable to type 'Demo<{ a: number; }>'. + Types of parameters 'key' and 'key' are incompatible. + Type '"a"' is not assignable to type '"b"'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(127,1): error TS2322: Type 'Demo<{ a: number; b: string; }>' is not assignable to type 'Demo<{ a: number; }>'. + Property 'b' is missing in type '{ a: number; }' but required in type '{ a: number; b: string; }'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(128,1): error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ b: string; }>'. + Types of parameters 'key' and 'key' are incompatible. + Type '"b"' is not assignable to type '"a"'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(129,1): error TS2322: Type 'Demo<{ a: number; b: string; }>' is not assignable to type 'Demo<{ b: string; }>'. + Property 'a' is missing in type '{ b: string; }' but required in type '{ a: number; b: string; }'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(130,1): error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ a: number; b: string; }>'. + Types of parameters 'key' and 'key' are incompatible. + Type '"a" | "b"' is not assignable to type '"a"'. + Type '"b"' is not assignable to type '"a"'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(131,1): error TS2322: Type 'Demo<{ b: string; }>' is not assignable to type 'Demo<{ a: number; b: string; }>'. + Types of parameters 'key' and 'key' are incompatible. + Type '"a" | "b"' is not assignable to type '"b"'. + Type '"a"' is not assignable to type '"b"'. ==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts (29 errors) ==== @@ -210,23 +228,19 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS23 !!! error TS2322: Type 'Demo<{ b: string; }>' is not assignable to type 'Demo<{ a: number; }>'. !!! error TS2322: Types of parameters 'key' and 'key' are incompatible. !!! error TS2322: Type '"a"' is not assignable to type '"b"'. -!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:122:24: 'a' is declared here. da = dc; ~~ !!! error TS2322: Type 'Demo<{ a: number; b: string; }>' is not assignable to type 'Demo<{ a: number; }>'. !!! error TS2322: Property 'b' is missing in type '{ a: number; }' but required in type '{ a: number; b: string; }'. -!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:124:35: 'b' is declared here. db = da; // Error ~~ !!! error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ b: string; }>'. !!! error TS2322: Types of parameters 'key' and 'key' are incompatible. !!! error TS2322: Type '"b"' is not assignable to type '"a"'. -!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:123:24: 'b' is declared here. db = dc; ~~ !!! error TS2322: Type 'Demo<{ a: number; b: string; }>' is not assignable to type 'Demo<{ b: string; }>'. !!! error TS2322: Property 'a' is missing in type '{ b: string; }' but required in type '{ a: number; b: string; }'. -!!! related TS2728 tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts:124:24: 'a' is declared here. dc = da; // Error ~~ !!! error TS2322: Type 'Demo<{ a: number; }>' is not assignable to type 'Demo<{ a: number; b: string; }>'. diff --git a/tests/baselines/reference/keyofAndIndexedAccess2.symbols b/tests/baselines/reference/keyofAndIndexedAccess2.symbols index 619e7e3f87e51..2acf707c38da0 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess2.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess2.symbols @@ -566,7 +566,7 @@ export class c { constructor() { this.a = "b"; ->this : Symbol(c, Decl(keyofAndIndexedAccess2.ts, 136, 1)) +>this : Symbol(c, Decl(keyofAndIndexedAccess2.ts, 151, 1)) this["a"] = "b"; >this : Symbol(c, Decl(keyofAndIndexedAccess2.ts, 151, 1)) @@ -576,67 +576,67 @@ export class c { // Repro from #31385 type Foo = { [key: string]: { [K in keyof T]: K }[keyof T] }; ->Foo : Symbol(Foo, Decl(keyofAndIndexedAccess2.ts, 146, 1)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 150, 9)) ->key : Symbol(key, Decl(keyofAndIndexedAccess2.ts, 150, 17)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 150, 34)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 150, 9)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 150, 34)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 150, 9)) +>Foo : Symbol(Foo, Decl(keyofAndIndexedAccess2.ts, 161, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 165, 9)) +>key : Symbol(key, Decl(keyofAndIndexedAccess2.ts, 165, 17)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 165, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 165, 9)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 165, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 165, 9)) type Bar = { [key: string]: { [K in keyof T]: [K] }[keyof T] }; ->Bar : Symbol(Bar, Decl(keyofAndIndexedAccess2.ts, 150, 64)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 152, 9)) ->key : Symbol(key, Decl(keyofAndIndexedAccess2.ts, 152, 17)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 152, 34)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 152, 9)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 152, 34)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 152, 9)) +>Bar : Symbol(Bar, Decl(keyofAndIndexedAccess2.ts, 165, 64)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 167, 9)) +>key : Symbol(key, Decl(keyofAndIndexedAccess2.ts, 167, 17)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 167, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 167, 9)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 167, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 167, 9)) type Baz> = { [K in keyof Q]: T[Q[K]] }; ->Baz : Symbol(Baz, Decl(keyofAndIndexedAccess2.ts, 152, 66)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 154, 9)) ->Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 154, 11)) ->Foo : Symbol(Foo, Decl(keyofAndIndexedAccess2.ts, 146, 1)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 154, 9)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 154, 35)) ->Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 154, 11)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 154, 9)) ->Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 154, 11)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 154, 35)) +>Baz : Symbol(Baz, Decl(keyofAndIndexedAccess2.ts, 167, 66)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 169, 9)) +>Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 169, 11)) +>Foo : Symbol(Foo, Decl(keyofAndIndexedAccess2.ts, 161, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 169, 9)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 169, 35)) +>Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 169, 11)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 169, 9)) +>Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 169, 11)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 169, 35)) type Qux> = { [K in keyof Q]: T[Q[K]["0"]] }; ->Qux : Symbol(Qux, Decl(keyofAndIndexedAccess2.ts, 154, 60)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 156, 9)) ->Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 156, 11)) ->Bar : Symbol(Bar, Decl(keyofAndIndexedAccess2.ts, 150, 64)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 156, 9)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 156, 35)) ->Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 156, 11)) ->T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 156, 9)) ->Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 156, 11)) ->K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 156, 35)) +>Qux : Symbol(Qux, Decl(keyofAndIndexedAccess2.ts, 169, 60)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 171, 9)) +>Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 171, 11)) +>Bar : Symbol(Bar, Decl(keyofAndIndexedAccess2.ts, 165, 64)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 171, 9)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 171, 35)) +>Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 171, 11)) +>T : Symbol(T, Decl(keyofAndIndexedAccess2.ts, 171, 9)) +>Q : Symbol(Q, Decl(keyofAndIndexedAccess2.ts, 171, 11)) +>K : Symbol(K, Decl(keyofAndIndexedAccess2.ts, 171, 35)) // Repro from #32038 const actions = ['resizeTo', 'resizeBy'] as const; ->actions : Symbol(actions, Decl(keyofAndIndexedAccess2.ts, 160, 5)) +>actions : Symbol(actions, Decl(keyofAndIndexedAccess2.ts, 175, 5)) for (const action of actions) { ->action : Symbol(action, Decl(keyofAndIndexedAccess2.ts, 161, 10)) ->actions : Symbol(actions, Decl(keyofAndIndexedAccess2.ts, 160, 5)) +>action : Symbol(action, Decl(keyofAndIndexedAccess2.ts, 176, 10)) +>actions : Symbol(actions, Decl(keyofAndIndexedAccess2.ts, 175, 5)) window[action] = (x, y) => { >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) ->action : Symbol(action, Decl(keyofAndIndexedAccess2.ts, 161, 10)) ->x : Symbol(x, Decl(keyofAndIndexedAccess2.ts, 162, 19)) ->y : Symbol(y, Decl(keyofAndIndexedAccess2.ts, 162, 21)) +>action : Symbol(action, Decl(keyofAndIndexedAccess2.ts, 176, 10)) +>x : Symbol(x, Decl(keyofAndIndexedAccess2.ts, 177, 19)) +>y : Symbol(y, Decl(keyofAndIndexedAccess2.ts, 177, 21)) window[action](x, y); >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) ->action : Symbol(action, Decl(keyofAndIndexedAccess2.ts, 161, 10)) ->x : Symbol(x, Decl(keyofAndIndexedAccess2.ts, 162, 19)) ->y : Symbol(y, Decl(keyofAndIndexedAccess2.ts, 162, 21)) +>action : Symbol(action, Decl(keyofAndIndexedAccess2.ts, 176, 10)) +>x : Symbol(x, Decl(keyofAndIndexedAccess2.ts, 177, 19)) +>y : Symbol(y, Decl(keyofAndIndexedAccess2.ts, 177, 21)) } }