Skip to content

Commit

Permalink
Bail on 0- and 1-length lists in removeSubtypes to avoid spurious c…
Browse files Browse the repository at this point in the history
…ircularity problems (#46981)

* Creates a reasonable workaround for #46939

* Remove unrelated newline

* Do the cheap length check ahead of the cache check

* Actually do it.
  • Loading branch information
RyanCavanaugh authored Mar 8, 2022
1 parent d8b21a8 commit af027f3
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14393,11 +14393,17 @@ 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;
}

// 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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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))
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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))
}
Original file line number Diff line number Diff line change
@@ -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
}
16 changes: 16 additions & 0 deletions tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 16 additions & 0 deletions tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck2.ts
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit af027f3

Please sign in to comment.