From cbc777ef4c059639c5056468ef63c64518ad407a Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 1 Dec 2021 13:18:05 -0800 Subject: [PATCH 1/4] Creates a reasonable workaround for #46939 --- src/compiler/checker.ts | 6 +++ ...btypeReductionNoStructuralCheck.errors.txt | 19 ++++++++ ...rivialSubtypeReductionNoStructuralCheck.js | 43 +++++++++++++++++++ ...lSubtypeReductionNoStructuralCheck.symbols | 31 +++++++++++++ ...ialSubtypeReductionNoStructuralCheck.types | 29 +++++++++++++ ...ivialSubtypeReductionNoStructuralCheck2.js | 43 +++++++++++++++++++ ...SubtypeReductionNoStructuralCheck2.symbols | 32 ++++++++++++++ ...alSubtypeReductionNoStructuralCheck2.types | 30 +++++++++++++ ...rivialSubtypeReductionNoStructuralCheck.ts | 16 +++++++ ...ivialSubtypeReductionNoStructuralCheck2.ts | 16 +++++++ 10 files changed, 265 insertions(+) create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.js create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.symbols create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.js create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.symbols create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.types create mode 100644 tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts create mode 100644 tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2e0f1ef628101..dd4bf92b97f5a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14203,6 +14203,11 @@ namespace ts { if (match) { return match; } + // [] and [T] immediately reduce to [] and [T] respectively + if (types.length < 2) { + return types; + } + // We assume that redundant primitive types have already been removed from the types array and that there // are no any and unknown types in the array. Thus, the only possible supertypes for primitive types are empty // object types, and if none of those are present we can exclude primitive types from the subtype check. @@ -14328,6 +14333,7 @@ namespace ts { if (types.length === 1) { return types[0]; } + let typeSet: Type[] | undefined = []; const includes = addTypesToUnion(typeSet, 0, types); if (unionReduction !== UnionReduction.None) { diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt new file mode 100644 index 0000000000000..279e221ad70c3 --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt @@ -0,0 +1,19 @@ +tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts(3,7): error TS7023: 'steps' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + + +==== tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts (1 errors) ==== + declare const props: WizardStepProps; + export class Wizard { + get steps() { + ~~~~~ +!!! error TS7023: 'steps' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + return { + wizard: this, + ...props, + } as WizardStepProps; + } + } + + export interface WizardStepProps { + wizard?: Wizard; + } \ No newline at end of file diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.js b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.js new file mode 100644 index 0000000000000..e1318004aadbf --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.js @@ -0,0 +1,43 @@ +//// [trivialSubtypeReductionNoStructuralCheck.ts] +declare const props: WizardStepProps; +export class Wizard { + get steps() { + return { + wizard: this, + ...props, + } as WizardStepProps; + } +} + +export interface WizardStepProps { + wizard?: Wizard; +} + +//// [trivialSubtypeReductionNoStructuralCheck.js] +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Wizard = void 0; +var Wizard = /** @class */ (function () { + function Wizard() { + } + Object.defineProperty(Wizard.prototype, "steps", { + get: function () { + return __assign({ wizard: this }, props); + }, + enumerable: false, + configurable: true + }); + return Wizard; +}()); +exports.Wizard = Wizard; diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.symbols b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.symbols new file mode 100644 index 0000000000000..7e48b7383b8d7 --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.symbols @@ -0,0 +1,31 @@ +=== tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts === +declare const props: WizardStepProps; +>props : Symbol(props, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 0, 13)) +>WizardStepProps : Symbol(WizardStepProps, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 8, 1)) + +export class Wizard { +>Wizard : Symbol(Wizard, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 0, 37)) + + get steps() { +>steps : Symbol(Wizard.steps, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 1, 21)) + + return { + wizard: this, +>wizard : Symbol(wizard, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 3, 12)) +>this : Symbol(Wizard, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 0, 37)) + + ...props, +>props : Symbol(props, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 0, 13)) + + } as WizardStepProps; +>WizardStepProps : Symbol(WizardStepProps, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 8, 1)) + } +} + +export interface WizardStepProps { +>WizardStepProps : Symbol(WizardStepProps, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 8, 1)) + + wizard?: Wizard; +>wizard : Symbol(WizardStepProps.wizard, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 10, 34)) +>Wizard : Symbol(Wizard, Decl(trivialSubtypeReductionNoStructuralCheck.ts, 0, 37)) +} diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types new file mode 100644 index 0000000000000..41d7b304624d2 --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts === +declare const props: WizardStepProps; +>props : WizardStepProps + +export class Wizard { +>Wizard : Wizard + + get steps() { +>steps : any + + return { +>{ wizard: this, ...props, } as WizardStepProps : WizardStepProps +>{ wizard: this, ...props, } : { wizard: Wizard; } + + wizard: this, +>wizard : this +>this : this + + ...props, +>props : WizardStepProps + + } as WizardStepProps; + } +} + +export interface WizardStepProps { + wizard?: Wizard; +>wizard : Wizard | undefined +} diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.js b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.js new file mode 100644 index 0000000000000..022844b4c2223 --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.js @@ -0,0 +1,43 @@ +//// [trivialSubtypeReductionNoStructuralCheck2.ts] +declare const props: WizardStepProps; +export class Wizard { + get steps() { + return { + wizard: this as Wizard, + ...props, + } as WizardStepProps; + } +} + +export interface WizardStepProps { + wizard?: Wizard; +} + +//// [trivialSubtypeReductionNoStructuralCheck2.js] +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Wizard = void 0; +var Wizard = /** @class */ (function () { + function Wizard() { + } + Object.defineProperty(Wizard.prototype, "steps", { + get: function () { + return __assign({ wizard: this }, props); + }, + enumerable: false, + configurable: true + }); + return Wizard; +}()); +exports.Wizard = Wizard; diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.symbols b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.symbols new file mode 100644 index 0000000000000..f52572a937a6f --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.symbols @@ -0,0 +1,32 @@ +=== tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck2.ts === +declare const props: WizardStepProps; +>props : Symbol(props, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 0, 13)) +>WizardStepProps : Symbol(WizardStepProps, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 8, 1)) + +export class Wizard { +>Wizard : Symbol(Wizard, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 0, 37)) + + get steps() { +>steps : Symbol(Wizard.steps, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 1, 21)) + + return { + wizard: this as Wizard, +>wizard : Symbol(wizard, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 3, 12)) +>this : Symbol(Wizard, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 0, 37)) +>Wizard : Symbol(Wizard, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 0, 37)) + + ...props, +>props : Symbol(props, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 0, 13)) + + } as WizardStepProps; +>WizardStepProps : Symbol(WizardStepProps, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 8, 1)) + } +} + +export interface WizardStepProps { +>WizardStepProps : Symbol(WizardStepProps, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 8, 1)) + + wizard?: Wizard; +>wizard : Symbol(WizardStepProps.wizard, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 10, 34)) +>Wizard : Symbol(Wizard, Decl(trivialSubtypeReductionNoStructuralCheck2.ts, 0, 37)) +} diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.types b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.types new file mode 100644 index 0000000000000..2ea6acdd0b99f --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck2.types @@ -0,0 +1,30 @@ +=== tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck2.ts === +declare const props: WizardStepProps; +>props : WizardStepProps + +export class Wizard { +>Wizard : Wizard + + get steps() { +>steps : WizardStepProps + + return { +>{ wizard: this as Wizard, ...props, } as WizardStepProps : WizardStepProps +>{ wizard: this as Wizard, ...props, } : { wizard: Wizard; } + + wizard: this as Wizard, +>wizard : Wizard +>this as Wizard : Wizard +>this : this + + ...props, +>props : WizardStepProps + + } as WizardStepProps; + } +} + +export interface WizardStepProps { + wizard?: Wizard; +>wizard : Wizard | undefined +} diff --git a/tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts b/tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts new file mode 100644 index 0000000000000..b9917996dee76 --- /dev/null +++ b/tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts @@ -0,0 +1,16 @@ +// @strict: true +// @target: es5 + +declare const props: WizardStepProps; +export class Wizard { + get steps() { + return { + wizard: this, + ...props, + } as WizardStepProps; + } +} + +export interface WizardStepProps { + wizard?: Wizard; +} \ No newline at end of file diff --git a/tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck2.ts b/tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck2.ts new file mode 100644 index 0000000000000..2c8e0ee2c64d0 --- /dev/null +++ b/tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck2.ts @@ -0,0 +1,16 @@ +// @strict: true +// @target: es5 + +declare const props: WizardStepProps; +export class Wizard { + get steps() { + return { + wizard: this as Wizard, + ...props, + } as WizardStepProps; + } +} + +export interface WizardStepProps { + wizard?: Wizard; +} \ No newline at end of file From 80d1bb5d4c249367bcc127c5ff13515c6cdcc6b0 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 1 Dec 2021 13:19:54 -0800 Subject: [PATCH 2/4] Remove unrelated newline --- src/compiler/checker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd4bf92b97f5a..cb0c42457f24a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14333,7 +14333,6 @@ namespace ts { if (types.length === 1) { return types[0]; } - let typeSet: Type[] | undefined = []; const includes = addTypesToUnion(typeSet, 0, types); if (unionReduction !== UnionReduction.None) { From bf23e4d396190cd5af61e5f149b7bc4124ea9140 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 1 Dec 2021 13:47:41 -0800 Subject: [PATCH 3/4] Do the cheap length check ahead of the cache check --- src/compiler/checker.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cb0c42457f24a..30a22b95b4593 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14198,15 +14198,16 @@ namespace ts { } function removeSubtypes(types: Type[], hasObjectTypes: boolean): Type[] | undefined { + // [] and [T] immediately reduce to [] and [T] respectively + if (types.length < 2) { + // return types; + } + const id = getTypeListId(types); const match = subtypeReductionCache.get(id); if (match) { return match; } - // [] and [T] immediately reduce to [] and [T] respectively - if (types.length < 2) { - return types; - } // We assume that redundant primitive types have already been removed from the types array and that there // are no any and unknown types in the array. Thus, the only possible supertypes for primitive types are empty From dd91ab73adbfeb1eda1f1e243fc3d8e87f04f1ec Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 1 Dec 2021 13:50:19 -0800 Subject: [PATCH 4/4] Actually do it. --- 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 30a22b95b4593..1c6e80e7c82a0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14200,7 +14200,7 @@ namespace ts { function removeSubtypes(types: Type[], hasObjectTypes: boolean): Type[] | undefined { // [] and [T] immediately reduce to [] and [T] respectively if (types.length < 2) { - // return types; + return types; } const id = getTypeListId(types);