Track tuple type recursion in inferFromObjectTypes (#37479)

* Track recursive tuple types in inferFromObjectTypes

* Add regression test
This commit is contained in:
Anders Hejlsberg
2020-03-19 14:05:33 -07:00
committed by GitHub
parent 7e07a2b5d1
commit e15a9fb3a8
6 changed files with 209 additions and 6 deletions

View File

@@ -18131,7 +18131,7 @@ namespace ts {
}
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
let symbolStack: Symbol[];
let symbolOrTypeStack: (Symbol | Type)[];
let visited: Map<number>;
let bivariant = false;
let propagationType: Type;
@@ -18570,15 +18570,15 @@ namespace ts {
// its symbol with the instance side which would lead to false positives.
const isNonConstructorObject = target.flags & TypeFlags.Object &&
!(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
const symbol = isNonConstructorObject ? target.symbol : undefined;
if (symbol) {
if (contains(symbolStack, symbol)) {
const symbolOrType = isNonConstructorObject ? isTupleType(target) ? target.target : target.symbol : undefined;
if (symbolOrType) {
if (contains(symbolOrTypeStack, symbolOrType)) {
inferencePriority = InferencePriority.Circularity;
return;
}
(symbolStack || (symbolStack = [])).push(symbol);
(symbolOrTypeStack || (symbolOrTypeStack = [])).push(symbolOrType);
inferFromObjectTypesWorker(source, target);
symbolStack.pop();
symbolOrTypeStack.pop();
}
else {
inferFromObjectTypesWorker(source, target);

View File

@@ -0,0 +1,36 @@
tests/cases/compiler/recursiveTupleTypeInference.ts(23,5): error TS2345: Argument of type '{ b: A; }' is not assignable to parameter of type 'G<{ b: unknown; }>'.
Types of property 'b' are incompatible.
Type 'A' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'.
Type '"number"' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'.
==== tests/cases/compiler/recursiveTupleTypeInference.ts (1 errors) ====
// Repro from #37475
export type A = "number" | "null" | A[];
export type F<T> = null extends T
? [F<NonNullable<T>>, "null"]
: T extends number
? "number"
: never;
export type G<T> = { [k in keyof T]: F<T[k]> };
interface K {
b: number | null;
}
const gK: { [key in keyof K]: A } = { b: ["number", "null"] };
function foo<T>(g: G<T>): T {
return {} as any;
}
foo(gK);
~~
!!! error TS2345: Argument of type '{ b: A; }' is not assignable to parameter of type 'G<{ b: unknown; }>'.
!!! error TS2345: Types of property 'b' are incompatible.
!!! error TS2345: Type 'A' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'.
!!! error TS2345: Type '"number"' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'.

View File

@@ -0,0 +1,35 @@
//// [recursiveTupleTypeInference.ts]
// Repro from #37475
export type A = "number" | "null" | A[];
export type F<T> = null extends T
? [F<NonNullable<T>>, "null"]
: T extends number
? "number"
: never;
export type G<T> = { [k in keyof T]: F<T[k]> };
interface K {
b: number | null;
}
const gK: { [key in keyof K]: A } = { b: ["number", "null"] };
function foo<T>(g: G<T>): T {
return {} as any;
}
foo(gK);
//// [recursiveTupleTypeInference.js]
"use strict";
// Repro from #37475
exports.__esModule = true;
var gK = { b: ["number", "null"] };
function foo(g) {
return {};
}
foo(gK);

View File

@@ -0,0 +1,61 @@
=== tests/cases/compiler/recursiveTupleTypeInference.ts ===
// Repro from #37475
export type A = "number" | "null" | A[];
>A : Symbol(A, Decl(recursiveTupleTypeInference.ts, 0, 0))
>A : Symbol(A, Decl(recursiveTupleTypeInference.ts, 0, 0))
export type F<T> = null extends T
>F : Symbol(F, Decl(recursiveTupleTypeInference.ts, 2, 40))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14))
? [F<NonNullable<T>>, "null"]
>F : Symbol(F, Decl(recursiveTupleTypeInference.ts, 2, 40))
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14))
: T extends number
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14))
? "number"
: never;
export type G<T> = { [k in keyof T]: F<T[k]> };
>G : Symbol(G, Decl(recursiveTupleTypeInference.ts, 8, 12))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 10, 14))
>k : Symbol(k, Decl(recursiveTupleTypeInference.ts, 10, 22))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 10, 14))
>F : Symbol(F, Decl(recursiveTupleTypeInference.ts, 2, 40))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 10, 14))
>k : Symbol(k, Decl(recursiveTupleTypeInference.ts, 10, 22))
interface K {
>K : Symbol(K, Decl(recursiveTupleTypeInference.ts, 10, 47))
b: number | null;
>b : Symbol(K.b, Decl(recursiveTupleTypeInference.ts, 12, 13))
}
const gK: { [key in keyof K]: A } = { b: ["number", "null"] };
>gK : Symbol(gK, Decl(recursiveTupleTypeInference.ts, 16, 5))
>key : Symbol(key, Decl(recursiveTupleTypeInference.ts, 16, 13))
>K : Symbol(K, Decl(recursiveTupleTypeInference.ts, 10, 47))
>A : Symbol(A, Decl(recursiveTupleTypeInference.ts, 0, 0))
>b : Symbol(b, Decl(recursiveTupleTypeInference.ts, 16, 37))
function foo<T>(g: G<T>): T {
>foo : Symbol(foo, Decl(recursiveTupleTypeInference.ts, 16, 62))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 18, 13))
>g : Symbol(g, Decl(recursiveTupleTypeInference.ts, 18, 16))
>G : Symbol(G, Decl(recursiveTupleTypeInference.ts, 8, 12))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 18, 13))
>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 18, 13))
return {} as any;
}
foo(gK);
>foo : Symbol(foo, Decl(recursiveTupleTypeInference.ts, 16, 62))
>gK : Symbol(gK, Decl(recursiveTupleTypeInference.ts, 16, 5))

View File

@@ -0,0 +1,46 @@
=== tests/cases/compiler/recursiveTupleTypeInference.ts ===
// Repro from #37475
export type A = "number" | "null" | A[];
>A : A
export type F<T> = null extends T
>F : F<T>
>null : null
? [F<NonNullable<T>>, "null"]
: T extends number
? "number"
: never;
export type G<T> = { [k in keyof T]: F<T[k]> };
>G : G<T>
interface K {
b: number | null;
>b : number | null
>null : null
}
const gK: { [key in keyof K]: A } = { b: ["number", "null"] };
>gK : { b: A; }
>{ b: ["number", "null"] } : { b: ("number" | "null")[]; }
>b : ("number" | "null")[]
>["number", "null"] : ("number" | "null")[]
>"number" : "number"
>"null" : "null"
function foo<T>(g: G<T>): T {
>foo : <T>(g: G<T>) => T
>g : G<T>
return {} as any;
>{} as any : any
>{} : {}
}
foo(gK);
>foo(gK) : { b: unknown; }
>foo : <T>(g: G<T>) => T
>gK : { b: A; }

View File

@@ -0,0 +1,25 @@
// @strict: true
// Repro from #37475
export type A = "number" | "null" | A[];
export type F<T> = null extends T
? [F<NonNullable<T>>, "null"]
: T extends number
? "number"
: never;
export type G<T> = { [k in keyof T]: F<T[k]> };
interface K {
b: number | null;
}
const gK: { [key in keyof K]: A } = { b: ["number", "null"] };
function foo<T>(g: G<T>): T {
return {} as any;
}
foo(gK);