Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fastpath to isRelatedTo for type references #37481

Merged
83 changes: 49 additions & 34 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15340,11 +15340,23 @@ namespace ts {
* * Ternary.False if they are not related.
*/
function isRelatedTo(originalSource: Type, originalTarget: Type, reportErrors = false, headMessage?: DiagnosticMessage, intersectionState = IntersectionState.None): Ternary {
// Before normalization: if `source` is type reference (ergo an object), and `target` is primitive,
// skip all the checks we don't need and just return `isSimpleTypeRelatedTo` result
if (getObjectFlags(originalSource) & ObjectFlags.Reference && originalTarget.flags & TypeFlags.Primitive) {
weswigham marked this conversation as resolved.
Show resolved Hide resolved
if (isSimpleTypeRelatedTo(originalSource, originalTarget, relation, reportErrors ? reportError : undefined)) {
return Ternary.True;
}
else {
reportErrorResults(originalSource, originalTarget, Ternary.False, /*isComparingJsxAttributes*/ true);
weswigham marked this conversation as resolved.
Show resolved Hide resolved
return Ternary.False;
}
}

// Normalize the source and target types: Turn fresh literal types into regular literal types,
// 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 = getNormalizedType(originalSource, /*writing*/ false);
const source = getNormalizedType(originalSource, /*writing*/ false);
let target = getNormalizedType(originalTarget, /*writing*/ true);

if (source === target) return Ternary.True;
Expand Down Expand Up @@ -15476,45 +15488,48 @@ namespace ts {
}
}
}
reportErrorResults(source, target, result, isComparingJsxAttributes);
return result;

if (!result && reportErrors) {
source = originalSource.aliasSymbol ? originalSource : source;
target = originalTarget.aliasSymbol ? originalTarget : target;
let maybeSuppress = overrideNextErrorInfo > 0;
if (maybeSuppress) {
overrideNextErrorInfo--;
}
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
const currentError = errorInfo;
tryElaborateArrayLikeErrors(source, target, reportErrors);
if (errorInfo !== currentError) {
maybeSuppress = !!errorInfo;
function reportErrorResults(source: Type, target: Type, result: Ternary, isComparingJsxAttributes: boolean) {
if (!result && reportErrors) {
source = originalSource.aliasSymbol ? originalSource : source;
target = originalTarget.aliasSymbol ? originalTarget : target;
let maybeSuppress = overrideNextErrorInfo > 0;
if (maybeSuppress) {
overrideNextErrorInfo--;
}
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
const currentError = errorInfo;
tryElaborateArrayLikeErrors(source, target, reportErrors);
if (errorInfo !== currentError) {
maybeSuppress = !!errorInfo;
}
}
}
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
tryElaborateErrorsForPrimitivesAndObjects(source, target);
}
else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
}
else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) {
const targetTypes = (target as IntersectionType).types;
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType &&
(contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
// do not report top error
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
tryElaborateErrorsForPrimitivesAndObjects(source, target);
}
else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
}
else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) {
const targetTypes = (target as IntersectionType).types;
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType &&
(contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
// do not report top error
return result;
}
}
if (!headMessage && maybeSuppress) {
lastSkippedInfo = [source, target];
// Used by, eg, missing property checking to replace the top-level message with a more informative one
return result;
}
reportRelationError(headMessage, source, target);
}
if (!headMessage && maybeSuppress) {
lastSkippedInfo = [source, target];
// Used by, eg, missing property checking to replace the top-level message with a more informative one
return result;
}
reportRelationError(headMessage, source, target);
}
return result;
}

function isIdenticalTo(source: Type, target: Type): Ternary {
Expand Down
66 changes: 66 additions & 0 deletions tests/baselines/reference/recursiveArrayNotCircular.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//// [recursiveArrayNotCircular.ts]
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }

enum ActionType {
Foo,
Bar,
Baz,
Batch
}

type ReducerAction =
| Action<ActionType.Bar, number>
| Action<ActionType.Baz, boolean>
| Action<ActionType.Foo, string>
| Action<ActionType.Batch, ReducerAction[]>

function assertNever(a: never): never {
throw new Error("Unreachable!");
}

function reducer(action: ReducerAction): void {
switch(action.type) {
case ActionType.Bar:
const x: number = action.payload;
break;
case ActionType.Baz:
const y: boolean = action.payload;
break;
case ActionType.Foo:
const z: string = action.payload;
break;
case ActionType.Batch:
action.payload.map(reducer);
break;
default: return assertNever(action);
}
}

//// [recursiveArrayNotCircular.js]
var ActionType;
(function (ActionType) {
ActionType[ActionType["Foo"] = 0] = "Foo";
ActionType[ActionType["Bar"] = 1] = "Bar";
ActionType[ActionType["Baz"] = 2] = "Baz";
ActionType[ActionType["Batch"] = 3] = "Batch";
})(ActionType || (ActionType = {}));
function assertNever(a) {
throw new Error("Unreachable!");
}
function reducer(action) {
switch (action.type) {
case ActionType.Bar:
var x = action.payload;
break;
case ActionType.Baz:
var y = action.payload;
break;
case ActionType.Foo:
var z = action.payload;
break;
case ActionType.Batch:
action.payload.map(reducer);
break;
default: return assertNever(action);
}
}
126 changes: 126 additions & 0 deletions tests/baselines/reference/recursiveArrayNotCircular.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
=== tests/cases/compiler/recursiveArrayNotCircular.ts ===
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 38))
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53))
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))

enum ActionType {
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))

Foo,
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))

Bar,
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))

Baz,
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))

Batch
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
}

type ReducerAction =
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))

| Action<ActionType.Bar, number>
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))

| Action<ActionType.Baz, boolean>
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))

| Action<ActionType.Foo, string>
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))

| Action<ActionType.Batch, ReducerAction[]>
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))

function assertNever(a: never): never {
>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45))
>a : Symbol(a, Decl(recursiveArrayNotCircular.ts, 15, 21))

throw new Error("Unreachable!");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}

function reducer(action: ReducerAction): void {
>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1))
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))

switch(action.type) {
>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))
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
>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))

case ActionType.Bar:
>ActionType.Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))

const x: number = action.payload;
>x : Symbol(x, Decl(recursiveArrayNotCircular.ts, 22, 17))
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))

break;
case ActionType.Baz:
>ActionType.Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))

const y: boolean = action.payload;
>y : Symbol(y, Decl(recursiveArrayNotCircular.ts, 25, 17))
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62))
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62))

break;
case ActionType.Foo:
>ActionType.Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))

const z: string = action.payload;
>z : Symbol(z, Decl(recursiveArrayNotCircular.ts, 28, 17))
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))

break;
case ActionType.Batch:
>ActionType.Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))

action.payload.map(reducer);
>action.payload.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1))

break;
default: return assertNever(action);
>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45))
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
}
}
Loading