Skip to content

Commit edd4e0a

Browse files
authored
Add fastpath to isRelatedTo for type references (#37481)
* Add fastpath to isRelatedTo for type references * Do not check intersections or unions to ignore propegating reference flags, properly set comparing jsx flag * Re-remove unneeded check * Just check for TypeFlags.Object * Remove else clause
1 parent 8dd6b3a commit edd4e0a

File tree

5 files changed

+394
-37
lines changed

5 files changed

+394
-37
lines changed

src/compiler/checker.ts

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15541,11 +15541,21 @@ namespace ts {
1554115541
* * Ternary.False if they are not related.
1554215542
*/
1554315543
function isRelatedTo(originalSource: Type, originalTarget: Type, reportErrors = false, headMessage?: DiagnosticMessage, intersectionState = IntersectionState.None): Ternary {
15544+
// Before normalization: if `source` is type an object type, and `target` is primitive,
15545+
// skip all the checks we don't need and just return `isSimpleTypeRelatedTo` result
15546+
if (originalSource.flags & TypeFlags.Object && originalTarget.flags & TypeFlags.Primitive) {
15547+
if (isSimpleTypeRelatedTo(originalSource, originalTarget, relation, reportErrors ? reportError : undefined)) {
15548+
return Ternary.True;
15549+
}
15550+
reportErrorResults(originalSource, originalTarget, Ternary.False, !!(getObjectFlags(originalSource) & ObjectFlags.JsxAttributes));
15551+
return Ternary.False;
15552+
}
15553+
1554415554
// Normalize the source and target types: Turn fresh literal types into regular literal types,
1554515555
// turn deferred type references into regular type references, simplify indexed access and
1554615556
// conditional types, and resolve substitution types to either the substitution (on the source
1554715557
// side) or the type variable (on the target side).
15548-
let source = getNormalizedType(originalSource, /*writing*/ false);
15558+
const source = getNormalizedType(originalSource, /*writing*/ false);
1554915559
let target = getNormalizedType(originalTarget, /*writing*/ true);
1555015560

1555115561
if (source === target) return Ternary.True;
@@ -15679,6 +15689,7 @@ namespace ts {
1567915689
}
1568015690
}
1568115691
}
15692+
1568215693
// For certain combinations involving intersections and optional, excess, or mismatched properties we need
1568315694
// an extra property check where the intersection is viewed as a single object. The following are motivating
1568415695
// examples that all should be errors, but aren't without this extra property check:
@@ -15702,47 +15713,51 @@ namespace ts {
1570215713
inPropertyCheck = false;
1570315714
}
1570415715

15705-
if (!result && reportErrors) {
15706-
source = originalSource.aliasSymbol ? originalSource : source;
15707-
target = originalTarget.aliasSymbol ? originalTarget : target;
15708-
let maybeSuppress = overrideNextErrorInfo > 0;
15709-
if (maybeSuppress) {
15710-
overrideNextErrorInfo--;
15711-
}
15712-
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
15713-
const currentError = errorInfo;
15714-
tryElaborateArrayLikeErrors(source, target, reportErrors);
15715-
if (errorInfo !== currentError) {
15716-
maybeSuppress = !!errorInfo;
15716+
reportErrorResults(source, target, result, isComparingJsxAttributes);
15717+
return result;
15718+
15719+
function reportErrorResults(source: Type, target: Type, result: Ternary, isComparingJsxAttributes: boolean) {
15720+
if (!result && reportErrors) {
15721+
source = originalSource.aliasSymbol ? originalSource : source;
15722+
target = originalTarget.aliasSymbol ? originalTarget : target;
15723+
let maybeSuppress = overrideNextErrorInfo > 0;
15724+
if (maybeSuppress) {
15725+
overrideNextErrorInfo--;
15726+
}
15727+
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
15728+
const currentError = errorInfo;
15729+
tryElaborateArrayLikeErrors(source, target, reportErrors);
15730+
if (errorInfo !== currentError) {
15731+
maybeSuppress = !!errorInfo;
15732+
}
1571715733
}
15718-
}
15719-
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
15720-
tryElaborateErrorsForPrimitivesAndObjects(source, target);
15721-
}
15722-
else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
15723-
reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
15724-
}
15725-
else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) {
15726-
const targetTypes = (target as IntersectionType).types;
15727-
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
15728-
const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
15729-
if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType &&
15730-
(contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
15731-
// do not report top error
15734+
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
15735+
tryElaborateErrorsForPrimitivesAndObjects(source, target);
15736+
}
15737+
else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
15738+
reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
15739+
}
15740+
else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) {
15741+
const targetTypes = (target as IntersectionType).types;
15742+
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
15743+
const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
15744+
if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType &&
15745+
(contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
15746+
// do not report top error
15747+
return result;
15748+
}
15749+
}
15750+
else {
15751+
errorInfo = elaborateNeverIntersection(errorInfo, originalTarget);
15752+
}
15753+
if (!headMessage && maybeSuppress) {
15754+
lastSkippedInfo = [source, target];
15755+
// Used by, eg, missing property checking to replace the top-level message with a more informative one
1573215756
return result;
1573315757
}
15758+
reportRelationError(headMessage, source, target);
1573415759
}
15735-
else {
15736-
errorInfo = elaborateNeverIntersection(errorInfo, originalTarget);
15737-
}
15738-
if (!headMessage && maybeSuppress) {
15739-
lastSkippedInfo = [source, target];
15740-
// Used by, eg, missing property checking to replace the top-level message with a more informative one
15741-
return result;
15742-
}
15743-
reportRelationError(headMessage, source, target);
1574415760
}
15745-
return result;
1574615761
}
1574715762

1574815763
function isIdenticalTo(source: Type, target: Type): Ternary {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//// [recursiveArrayNotCircular.ts]
2+
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }
3+
4+
enum ActionType {
5+
Foo,
6+
Bar,
7+
Baz,
8+
Batch
9+
}
10+
11+
type ReducerAction =
12+
| Action<ActionType.Bar, number>
13+
| Action<ActionType.Baz, boolean>
14+
| Action<ActionType.Foo, string>
15+
| Action<ActionType.Batch, ReducerAction[]>
16+
17+
function assertNever(a: never): never {
18+
throw new Error("Unreachable!");
19+
}
20+
21+
function reducer(action: ReducerAction): void {
22+
switch(action.type) {
23+
case ActionType.Bar:
24+
const x: number = action.payload;
25+
break;
26+
case ActionType.Baz:
27+
const y: boolean = action.payload;
28+
break;
29+
case ActionType.Foo:
30+
const z: string = action.payload;
31+
break;
32+
case ActionType.Batch:
33+
action.payload.map(reducer);
34+
break;
35+
default: return assertNever(action);
36+
}
37+
}
38+
39+
//// [recursiveArrayNotCircular.js]
40+
var ActionType;
41+
(function (ActionType) {
42+
ActionType[ActionType["Foo"] = 0] = "Foo";
43+
ActionType[ActionType["Bar"] = 1] = "Bar";
44+
ActionType[ActionType["Baz"] = 2] = "Baz";
45+
ActionType[ActionType["Batch"] = 3] = "Batch";
46+
})(ActionType || (ActionType = {}));
47+
function assertNever(a) {
48+
throw new Error("Unreachable!");
49+
}
50+
function reducer(action) {
51+
switch (action.type) {
52+
case ActionType.Bar:
53+
var x = action.payload;
54+
break;
55+
case ActionType.Baz:
56+
var y = action.payload;
57+
break;
58+
case ActionType.Foo:
59+
var z = action.payload;
60+
break;
61+
case ActionType.Batch:
62+
action.payload.map(reducer);
63+
break;
64+
default: return assertNever(action);
65+
}
66+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
=== tests/cases/compiler/recursiveArrayNotCircular.ts ===
2+
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }
3+
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
4+
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
5+
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
6+
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
7+
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 38))
8+
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
9+
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53))
10+
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
11+
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
12+
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
13+
14+
enum ActionType {
15+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
16+
17+
Foo,
18+
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
19+
20+
Bar,
21+
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
22+
23+
Baz,
24+
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
25+
26+
Batch
27+
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
28+
}
29+
30+
type ReducerAction =
31+
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))
32+
33+
| Action<ActionType.Bar, number>
34+
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
35+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
36+
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
37+
38+
| Action<ActionType.Baz, boolean>
39+
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
40+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
41+
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
42+
43+
| Action<ActionType.Foo, string>
44+
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
45+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
46+
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
47+
48+
| Action<ActionType.Batch, ReducerAction[]>
49+
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
50+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
51+
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
52+
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))
53+
54+
function assertNever(a: never): never {
55+
>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45))
56+
>a : Symbol(a, Decl(recursiveArrayNotCircular.ts, 15, 21))
57+
58+
throw new Error("Unreachable!");
59+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
60+
}
61+
62+
function reducer(action: ReducerAction): void {
63+
>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1))
64+
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
65+
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))
66+
67+
switch(action.type) {
68+
>action.type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53))
69+
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
70+
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53))
71+
72+
case ActionType.Bar:
73+
>ActionType.Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
74+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
75+
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
76+
77+
const x: number = action.payload;
78+
>x : Symbol(x, Decl(recursiveArrayNotCircular.ts, 22, 17))
79+
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
80+
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
81+
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
82+
83+
break;
84+
case ActionType.Baz:
85+
>ActionType.Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
86+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
87+
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
88+
89+
const y: boolean = action.payload;
90+
>y : Symbol(y, Decl(recursiveArrayNotCircular.ts, 25, 17))
91+
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62))
92+
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
93+
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62))
94+
95+
break;
96+
case ActionType.Foo:
97+
>ActionType.Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
98+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
99+
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
100+
101+
const z: string = action.payload;
102+
>z : Symbol(z, Decl(recursiveArrayNotCircular.ts, 28, 17))
103+
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
104+
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
105+
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
106+
107+
break;
108+
case ActionType.Batch:
109+
>ActionType.Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
110+
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
111+
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
112+
113+
action.payload.map(reducer);
114+
>action.payload.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
115+
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
116+
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
117+
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
118+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
119+
>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1))
120+
121+
break;
122+
default: return assertNever(action);
123+
>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45))
124+
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
125+
}
126+
}

0 commit comments

Comments
 (0)