Skip to content

Commit

Permalink
Merge pull request #33678 from microsoft/fix33617
Browse files Browse the repository at this point in the history
Property handle recursive type references in type inference
  • Loading branch information
ahejlsberg committed Sep 30, 2019
2 parents 7f3c03d + 2a4d267 commit 7ce793c
Show file tree
Hide file tree
Showing 6 changed files with 687 additions and 3 deletions.
11 changes: 9 additions & 2 deletions src/compiler/checker.ts
Expand Up @@ -17070,7 +17070,7 @@ namespace ts {
function couldContainTypeVariables(type: Type): boolean {
const objectFlags = getObjectFlags(type);
return !!(type.flags & TypeFlags.Instantiable ||
objectFlags & ObjectFlags.Reference && forEach(getTypeArguments(<TypeReference>type), couldContainTypeVariables) ||
objectFlags & ObjectFlags.Reference && ((<TypeReference>type).node || forEach(getTypeArguments(<TypeReference>type), couldContainTypeVariables)) ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ||
objectFlags & ObjectFlags.Mapped ||
type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
Expand Down Expand Up @@ -17367,7 +17367,8 @@ namespace ts {
}
}
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target)) &&
!((<TypeReference>source).node && (<TypeReference>target).node)) {
// If source and target are references to the same generic type, infer from type arguments
inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target));
}
Expand Down Expand Up @@ -17648,6 +17649,12 @@ namespace ts {
}

function inferFromObjectTypesWorker(source: Type, target: Type) {
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
// If source and target are references to the same generic type, infer from type arguments
inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target));
return;
}
if (isGenericMappedType(source) && isGenericMappedType(target)) {
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
// from S to T and from X to Y.
Expand Down
79 changes: 78 additions & 1 deletion tests/baselines/reference/recursiveTypeReferences1.errors.txt
Expand Up @@ -2,9 +2,18 @@ tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeRefe
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(60,7): error TS2322: Type 'number' is not assignable to type 'string | RecArray<string>'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(66,8): error TS2322: Type 'number' is not assignable to type 'string | string[]'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(72,8): error TS2322: Type 'number' is not assignable to type 'string | (string | string[])[]'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(102,10): error TS2304: Cannot find name 'html'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(104,12): error TS2304: Cannot find name 'html'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(105,7): error TS2304: Cannot find name 'html'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(106,52): error TS2304: Cannot find name 'frag'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(116,11): error TS2304: Cannot find name 'concat'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(116,24): error TS2304: Cannot find name 'concat'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(117,11): error TS2304: Cannot find name 'concat'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(122,11): error TS2304: Cannot find name 'concat'.
tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts(127,3): error TS2304: Cannot find name 'assert'.


==== tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts (4 errors) ====
==== tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts (13 errors) ====
type ValueOrArray<T> = T | Array<ValueOrArray<T>>;

const a0: ValueOrArray<number> = 1;
Expand Down Expand Up @@ -92,4 +101,72 @@ tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeRefe
type T13 = T13[] | string;
type T14 = T14[] & { x: string };
type T15<X> = X extends string ? T15<X>[] : never;

type ValueOrArray1<T> = T | ValueOrArray1<T>[];
type ValueOrArray2<T> = T | ValueOrArray2<T>[];

declare function foo1<T>(a: ValueOrArray1<T>): T;
declare let ra1: ValueOrArray2<string>;

let x1 = foo1(ra1); // Boom!

type NumberOrArray1<T> = T | ValueOrArray1<T>[];
type NumberOrArray2<T> = T | ValueOrArray2<T>[];

declare function foo2<T>(a: ValueOrArray1<T>): T;
declare let ra2: ValueOrArray2<string>;

let x2 = foo2(ra2); // Boom!

// Repro from #33617 (errors are expected)

type Tree = [HTMLHeadingElement, Tree][];

function parse(node: Tree, index: number[] = []): HTMLUListElement {
return html('ul', node.map(([el, children], i) => {
~~~~
!!! error TS2304: Cannot find name 'html'.
const idx = [...index, i + 1];
return html('li', [
~~~~
!!! error TS2304: Cannot find name 'html'.
html('a', { href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent!),
~~~~
!!! error TS2304: Cannot find name 'html'.
children.length > 0 ? parse(children, idx) : frag()
~~~~
!!! error TS2304: Cannot find name 'frag'.
]);
}));
}

function cons(hs: HTMLHeadingElement[]): Tree {
return hs
.reduce<HTMLHeadingElement[][]>((hss, h) => {
const hs = hss.pop()!;
return hs.length === 0 || level(h) > level(hs[0])
? concat(hss, [concat(hs, [h])])
~~~~~~
!!! error TS2304: Cannot find name 'concat'.
~~~~~~
!!! error TS2304: Cannot find name 'concat'.
: concat(hss, [hs, [h]]);
~~~~~~
!!! error TS2304: Cannot find name 'concat'.
}, [[]])
.reduce<Tree>((node, hs) =>
hs.length === 0
? node
: concat<Tree[number]>(node, [[hs.shift()!, cons(hs)]])
~~~~~~
!!! error TS2304: Cannot find name 'concat'.
, []);
}

function level(h: HTMLHeadingElement): number {
assert(isFinite(+h.tagName[1]));
~~~~~~
!!! error TS2304: Cannot find name 'assert'.
return +h.tagName[1];
}

102 changes: 102 additions & 0 deletions tests/baselines/reference/recursiveTypeReferences1.js
Expand Up @@ -78,10 +78,67 @@ type T12 = (T12)[];
type T13 = T13[] | string;
type T14 = T14[] & { x: string };
type T15<X> = X extends string ? T15<X>[] : never;

type ValueOrArray1<T> = T | ValueOrArray1<T>[];
type ValueOrArray2<T> = T | ValueOrArray2<T>[];

declare function foo1<T>(a: ValueOrArray1<T>): T;
declare let ra1: ValueOrArray2<string>;

let x1 = foo1(ra1); // Boom!

type NumberOrArray1<T> = T | ValueOrArray1<T>[];
type NumberOrArray2<T> = T | ValueOrArray2<T>[];

declare function foo2<T>(a: ValueOrArray1<T>): T;
declare let ra2: ValueOrArray2<string>;

let x2 = foo2(ra2); // Boom!

// Repro from #33617 (errors are expected)

type Tree = [HTMLHeadingElement, Tree][];

function parse(node: Tree, index: number[] = []): HTMLUListElement {
return html('ul', node.map(([el, children], i) => {
const idx = [...index, i + 1];
return html('li', [
html('a', { href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent!),
children.length > 0 ? parse(children, idx) : frag()
]);
}));
}

function cons(hs: HTMLHeadingElement[]): Tree {
return hs
.reduce<HTMLHeadingElement[][]>((hss, h) => {
const hs = hss.pop()!;
return hs.length === 0 || level(h) > level(hs[0])
? concat(hss, [concat(hs, [h])])
: concat(hss, [hs, [h]]);
}, [[]])
.reduce<Tree>((node, hs) =>
hs.length === 0
? node
: concat<Tree[number]>(node, [[hs.shift()!, cons(hs)]])
, []);
}

function level(h: HTMLHeadingElement): number {
assert(isFinite(+h.tagName[1]));
return +h.tagName[1];
}


//// [recursiveTypeReferences1.js]
"use strict";
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
var a0 = 1;
var a1 = [1, [2, 3], [4, [5, [6, 7]]]];
var hypertextNode = ["div", { id: "parent" },
Expand Down Expand Up @@ -124,6 +181,37 @@ flat2([[[0]]]); // number[]
flat2([1, 'a', [2]]); // (string | number)[]
flat2([1, [2, 'a']]); // (string | number)[]
flat2([1, ['a']]); // Error
var x1 = foo1(ra1); // Boom!
var x2 = foo2(ra2); // Boom!
function parse(node, index) {
if (index === void 0) { index = []; }
return html('ul', node.map(function (_a, i) {
var el = _a[0], children = _a[1];
var idx = __spreadArrays(index, [i + 1]);
return html('li', [
html('a', { href: "#" + el.id, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent),
children.length > 0 ? parse(children, idx) : frag()
]);
}));
}
function cons(hs) {
return hs
.reduce(function (hss, h) {
var hs = hss.pop();
return hs.length === 0 || level(h) > level(hs[0])
? concat(hss, [concat(hs, [h])])
: concat(hss, [hs, [h]]);
}, [[]])
.reduce(function (node, hs) {
return hs.length === 0
? node
: concat(node, [[hs.shift(), cons(hs)]]);
}, []);
}
function level(h) {
assert(isFinite(+h.tagName[1]));
return +h.tagName[1];
}


//// [recursiveTypeReferences1.d.ts]
Expand Down Expand Up @@ -165,3 +253,17 @@ declare type T14 = T14[] & {
x: string;
};
declare type T15<X> = X extends string ? T15<X>[] : never;
declare type ValueOrArray1<T> = T | ValueOrArray1<T>[];
declare type ValueOrArray2<T> = T | ValueOrArray2<T>[];
declare function foo1<T>(a: ValueOrArray1<T>): T;
declare let ra1: ValueOrArray2<string>;
declare let x1: string;
declare type NumberOrArray1<T> = T | ValueOrArray1<T>[];
declare type NumberOrArray2<T> = T | ValueOrArray2<T>[];
declare function foo2<T>(a: ValueOrArray1<T>): T;
declare let ra2: ValueOrArray2<string>;
declare let x2: string;
declare type Tree = [HTMLHeadingElement, Tree][];
declare function parse(node: Tree, index?: number[]): HTMLUListElement;
declare function cons(hs: HTMLHeadingElement[]): Tree;
declare function level(h: HTMLHeadingElement): number;

0 comments on commit 7ce793c

Please sign in to comment.