From 3b281005c1a3912ba5a4a8ec208c728f95abaf9c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 26 Jun 2019 14:03:05 -0700 Subject: [PATCH 1/5] Unwrap substitutions both before _and_ after potential simplification --- src/compiler/checker.ts | 8 +++ ...eyofNestedSimplifiedSubstituteUnwrapped.js | 50 +++++++++++++++ ...estedSimplifiedSubstituteUnwrapped.symbols | 62 +++++++++++++++++++ ...fNestedSimplifiedSubstituteUnwrapped.types | 39 ++++++++++++ ...eyofNestedSimplifiedSubstituteUnwrapped.ts | 22 +++++++ 5 files changed, 181 insertions(+) create mode 100644 tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.js create mode 100644 tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.symbols create mode 100644 tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.types create mode 100644 tests/cases/compiler/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 478c7b460756d..2ce6a388244f2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12788,6 +12788,14 @@ namespace ts { if (target.flags & TypeFlags.Simplifiable) { target = getSimplifiedType(target, /*writing*/ true); } + if (source.flags & TypeFlags.Substitution) { + source = (source).substitute; + } + if (target.flags & TypeFlags.Substitution) { + target = (target).typeVariable; + } + Debug.assert(!(source.flags & TypeFlags.Substitution), "Source type was a substitution - substitutes are unhandled in relationship checking"); + Debug.assert(!(target.flags & TypeFlags.Substitution), "Target type was a substitution - substitutes are unhandled in relationship checking"); // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`. // If so, reporting the `null` and `undefined` in the type is hardly useful. diff --git a/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.js b/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.js new file mode 100644 index 0000000000000..2ff1261fd794c --- /dev/null +++ b/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.js @@ -0,0 +1,50 @@ +//// [indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts] +type AnyFunction = (...args: any[]) => any; +type Params = Parameters>; + +interface Wrapper { + call(event: K, ...args: Params): void; +} + +interface AWrapped { + foo(): void; +} + +class A { + foo: Wrapper; +} + +interface BWrapped extends AWrapped { + bar(): void; +} + +class B extends A { + foo: Wrapper; +} + +//// [indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +var B = /** @class */ (function (_super) { + __extends(B, _super); + function B() { + return _super !== null && _super.apply(this, arguments) || this; + } + return B; +}(A)); diff --git a/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.symbols b/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.symbols new file mode 100644 index 0000000000000..41f6b4a5ebceb --- /dev/null +++ b/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.symbols @@ -0,0 +1,62 @@ +=== tests/cases/compiler/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts === +type AnyFunction = (...args: any[]) => any; +>AnyFunction : Symbol(AnyFunction, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 0, 0)) +>args : Symbol(args, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 0, 20)) + +type Params = Parameters>; +>Params : Symbol(Params, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 0, 43)) +>T : Symbol(T, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 1, 12)) +>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 1, 12)) +>AnyFunction : Symbol(AnyFunction, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 0, 0)) + +interface Wrapper { +>Wrapper : Symbol(Wrapper, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 1, 53)) +>T : Symbol(T, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 3, 18)) + + call(event: K, ...args: Params): void; +>call : Symbol(Wrapper.call, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 3, 22)) +>K : Symbol(K, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 4, 6)) +>T : Symbol(T, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 3, 18)) +>event : Symbol(event, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 4, 25)) +>K : Symbol(K, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 4, 6)) +>args : Symbol(args, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 4, 34)) +>Params : Symbol(Params, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 0, 43)) +>T : Symbol(T, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 3, 18)) +>K : Symbol(K, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 4, 6)) +} + +interface AWrapped { +>AWrapped : Symbol(AWrapped, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 5, 1)) + + foo(): void; +>foo : Symbol(AWrapped.foo, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 7, 20)) +} + +class A { +>A : Symbol(A, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 9, 1)) + + foo: Wrapper; +>foo : Symbol(A.foo, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 11, 9)) +>Wrapper : Symbol(Wrapper, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 1, 53)) +>AWrapped : Symbol(AWrapped, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 5, 1)) +} + +interface BWrapped extends AWrapped { +>BWrapped : Symbol(BWrapped, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 13, 1)) +>AWrapped : Symbol(AWrapped, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 5, 1)) + + bar(): void; +>bar : Symbol(BWrapped.bar, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 15, 37)) +} + +class B extends A { +>B : Symbol(B, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 17, 1)) +>A : Symbol(A, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 9, 1)) + + foo: Wrapper; +>foo : Symbol(B.foo, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 19, 19)) +>Wrapper : Symbol(Wrapper, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 1, 53)) +>BWrapped : Symbol(BWrapped, Decl(indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts, 13, 1)) +} diff --git a/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.types b/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.types new file mode 100644 index 0000000000000..2c57c390ddc53 --- /dev/null +++ b/tests/baselines/reference/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.types @@ -0,0 +1,39 @@ +=== tests/cases/compiler/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts === +type AnyFunction = (...args: any[]) => any; +>AnyFunction : AnyFunction +>args : any[] + +type Params = Parameters>; +>Params : Parameters> + +interface Wrapper { + call(event: K, ...args: Params): void; +>call : (event: K, ...args: Parameters>) => void +>event : K +>args : Parameters> +} + +interface AWrapped { + foo(): void; +>foo : () => void +} + +class A { +>A : A + + foo: Wrapper; +>foo : Wrapper +} + +interface BWrapped extends AWrapped { + bar(): void; +>bar : () => void +} + +class B extends A { +>B : B +>A : A + + foo: Wrapper; +>foo : Wrapper +} diff --git a/tests/cases/compiler/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts b/tests/cases/compiler/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts new file mode 100644 index 0000000000000..a68ea9beee6d2 --- /dev/null +++ b/tests/cases/compiler/indexedAccessKeyofNestedSimplifiedSubstituteUnwrapped.ts @@ -0,0 +1,22 @@ +type AnyFunction = (...args: any[]) => any; +type Params = Parameters>; + +interface Wrapper { + call(event: K, ...args: Params): void; +} + +interface AWrapped { + foo(): void; +} + +class A { + foo: Wrapper; +} + +interface BWrapped extends AWrapped { + bar(): void; +} + +class B extends A { + foo: Wrapper; +} \ No newline at end of file From e501cc92b26721fbc690cf7423ef0093c53f8d6e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 19 Aug 2019 12:31:51 -0700 Subject: [PATCH 2/5] Repeatedly unwrap/simplify until no more can be performed --- src/compiler/checker.ts | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ce6a388244f2..fb07e1b058b97 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12776,26 +12776,17 @@ namespace ts { if (isFreshLiteralType(target)) { target = (target).regularType; } - if (source.flags & TypeFlags.Substitution) { - source = (source).substitute; - } - if (target.flags & TypeFlags.Substitution) { - target = (target).typeVariable; - } - if (source.flags & TypeFlags.Simplifiable) { - source = getSimplifiedType(source, /*writing*/ false); - } - if (target.flags & TypeFlags.Simplifiable) { - target = getSimplifiedType(target, /*writing*/ true); - } - if (source.flags & TypeFlags.Substitution) { - source = (source).substitute; - } - if (target.flags & TypeFlags.Substitution) { - target = (target).typeVariable; + while (true) { + const s = source.flags & TypeFlags.Substitution ? source = (source).substitute : + source.flags & TypeFlags.Simplifiable ? getSimplifiedType(source, /*writing*/ false) : + source; + const t = target.flags & TypeFlags.Substitution ? (target).typeVariable : + target.flags & TypeFlags.Simplifiable ? getSimplifiedType(target, /*writing*/ true) : + target; + if (s === source && t === target) break; + source = s; + target = t; } - Debug.assert(!(source.flags & TypeFlags.Substitution), "Source type was a substitution - substitutes are unhandled in relationship checking"); - Debug.assert(!(target.flags & TypeFlags.Substitution), "Target type was a substitution - substitutes are unhandled in relationship checking"); // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`. // If so, reporting the `null` and `undefined` in the type is hardly useful. From a4fa0256415b67ffe48ea88c80308c142d62ff8d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 10 Feb 2020 15:59:08 -0800 Subject: [PATCH 3/5] Use seperate loops for source and target to reduce redundant calls --- src/compiler/checker.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a10ac9769ade7..c6559518952e6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15106,12 +15106,15 @@ namespace ts { // conditional types, and resolve substitution types to either the substitution (on the source // side) or the type variable (on the target side). let source = originalSource; - let target = originalTarget; do { const s = getNormalizedType(source, /*writing*/ false); - const t = getNormalizedType(target, /*writing*/ true); - if (s === source && t === target) break; + if (s === source) break; source = s; + } while (true); + let target = originalTarget; + do { + const t = getNormalizedType(target, /*writing*/ true); + if (t === target) break; target = t; } while (true); From f0394dd8a8ed87aa7f6682872ae2f00443158925 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 25 Feb 2020 17:16:56 -0800 Subject: [PATCH 4/5] Move loop into function --- src/compiler/checker.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5db505b9f4db2..92ac32cca4909 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14826,6 +14826,15 @@ namespace ts { } function getNormalizedType(type: Type, writing: boolean): Type { + do { + const t = getNormalizedTypeWorker(type, writing); + if (t === type) break; + type = t; + } while (true); + return type; + } + + function getNormalizedTypeWorker(type: Type, writing: boolean): Type { return isFreshLiteralType(type) ? (type).regularType : getObjectFlags(type) & ObjectFlags.Reference && (type).node ? createTypeReference((type).target, getTypeArguments(type)) : type.flags & TypeFlags.Substitution ? writing ? (type).typeVariable : (type).substitute : @@ -15143,18 +15152,8 @@ namespace ts { // turn deferred type references into regular type references, simplify indexed access and // conditional types, and resolve substitution types to either the substitution (on the source // side) or the type variable (on the target side). - let source = originalSource; - do { - const s = getNormalizedType(source, /*writing*/ false); - if (s === source) break; - source = s; - } while (true); - let target = originalTarget; - do { - const t = getNormalizedType(target, /*writing*/ true); - if (t === target) break; - target = t; - } while (true); + let source = getNormalizedType(originalSource, /*writing*/ false); + let target = getNormalizedType(originalTarget, /*writing*/ true); if (source === target) return Ternary.True; From d58ca4c79094d4ac75f1b6414bb747b5dceb66c0 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 25 Feb 2020 17:17:39 -0800 Subject: [PATCH 5/5] Inline worker --- src/compiler/checker.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 92ac32cca4909..43297244e1ad5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14827,21 +14827,17 @@ namespace ts { function getNormalizedType(type: Type, writing: boolean): Type { do { - const t = getNormalizedTypeWorker(type, writing); + const t = isFreshLiteralType(type) ? (type).regularType : + getObjectFlags(type) & ObjectFlags.Reference && (type).node ? createTypeReference((type).target, getTypeArguments(type)) : + type.flags & TypeFlags.Substitution ? writing ? (type).typeVariable : (type).substitute : + type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : + type; if (t === type) break; type = t; } while (true); return type; } - function getNormalizedTypeWorker(type: Type, writing: boolean): Type { - return isFreshLiteralType(type) ? (type).regularType : - getObjectFlags(type) & ObjectFlags.Reference && (type).node ? createTypeReference((type).target, getTypeArguments(type)) : - type.flags & TypeFlags.Substitution ? writing ? (type).typeVariable : (type).substitute : - type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : - type; - } - /** * Checks if 'source' is related to 'target' (e.g.: is a assignable to). * @param source The left-hand-side of the relation.