diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 39bd162503..97e08e7bcc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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(type), couldContainTypeVariables) || + objectFlags & ObjectFlags.Reference && ((type).node || forEach(getTypeArguments(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(type)); @@ -17367,7 +17367,8 @@ namespace ts { } } if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( - (source).target === (target).target || isArrayType(source) && isArrayType(target))) { + (source).target === (target).target || isArrayType(source) && isArrayType(target)) && + !((source).node && (target).node)) { // If source and target are references to the same generic type, infer from type arguments inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((source).target)); } @@ -17648,6 +17649,12 @@ namespace ts { } function inferFromObjectTypesWorker(source: Type, target: Type) { + if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( + (source).target === (target).target || isArrayType(source) && isArrayType(target))) { + // If source and target are references to the same generic type, infer from type arguments + inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((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. diff --git a/tests/baselines/reference/recursiveTypeReferences1.errors.txt b/tests/baselines/reference/recursiveTypeReferences1.errors.txt index 694f66b712..b3ab2e9b86 100644 --- a/tests/baselines/reference/recursiveTypeReferences1.errors.txt +++ b/tests/baselines/reference/recursiveTypeReferences1.errors.txt @@ -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'. 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 | Array>; const a0: ValueOrArray = 1; @@ -92,4 +101,72 @@ tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeRefe type T13 = T13[] | string; type T14 = T14[] & { x: string }; type T15 = X extends string ? T15[] : never; + + type ValueOrArray1 = T | ValueOrArray1[]; + type ValueOrArray2 = T | ValueOrArray2[]; + + declare function foo1(a: ValueOrArray1): T; + declare let ra1: ValueOrArray2; + + let x1 = foo1(ra1); // Boom! + + type NumberOrArray1 = T | ValueOrArray1[]; + type NumberOrArray2 = T | ValueOrArray2[]; + + declare function foo2(a: ValueOrArray1): T; + declare let ra2: ValueOrArray2; + + 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((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((node, hs) => + hs.length === 0 + ? node + : concat(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]; + } \ No newline at end of file diff --git a/tests/baselines/reference/recursiveTypeReferences1.js b/tests/baselines/reference/recursiveTypeReferences1.js index ac1280f543..7ac37c7070 100644 --- a/tests/baselines/reference/recursiveTypeReferences1.js +++ b/tests/baselines/reference/recursiveTypeReferences1.js @@ -78,10 +78,67 @@ type T12 = (T12)[]; type T13 = T13[] | string; type T14 = T14[] & { x: string }; type T15 = X extends string ? T15[] : never; + +type ValueOrArray1 = T | ValueOrArray1[]; +type ValueOrArray2 = T | ValueOrArray2[]; + +declare function foo1(a: ValueOrArray1): T; +declare let ra1: ValueOrArray2; + +let x1 = foo1(ra1); // Boom! + +type NumberOrArray1 = T | ValueOrArray1[]; +type NumberOrArray2 = T | ValueOrArray2[]; + +declare function foo2(a: ValueOrArray1): T; +declare let ra2: ValueOrArray2; + +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((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((node, hs) => + hs.length === 0 + ? node + : concat(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" }, @@ -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] @@ -165,3 +253,17 @@ declare type T14 = T14[] & { x: string; }; declare type T15 = X extends string ? T15[] : never; +declare type ValueOrArray1 = T | ValueOrArray1[]; +declare type ValueOrArray2 = T | ValueOrArray2[]; +declare function foo1(a: ValueOrArray1): T; +declare let ra1: ValueOrArray2; +declare let x1: string; +declare type NumberOrArray1 = T | ValueOrArray1[]; +declare type NumberOrArray2 = T | ValueOrArray2[]; +declare function foo2(a: ValueOrArray1): T; +declare let ra2: ValueOrArray2; +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; diff --git a/tests/baselines/reference/recursiveTypeReferences1.symbols b/tests/baselines/reference/recursiveTypeReferences1.symbols index a3f9907971..db0d7ca297 100644 --- a/tests/baselines/reference/recursiveTypeReferences1.symbols +++ b/tests/baselines/reference/recursiveTypeReferences1.symbols @@ -275,3 +275,205 @@ type T15 = X extends string ? T15[] : never; >T15 : Symbol(T15, Decl(recursiveTypeReferences1.ts, 77, 33)) >X : Symbol(X, Decl(recursiveTypeReferences1.ts, 78, 9)) +type ValueOrArray1 = T | ValueOrArray1[]; +>ValueOrArray1 : Symbol(ValueOrArray1, Decl(recursiveTypeReferences1.ts, 78, 50)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 80, 19)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 80, 19)) +>ValueOrArray1 : Symbol(ValueOrArray1, Decl(recursiveTypeReferences1.ts, 78, 50)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 80, 19)) + +type ValueOrArray2 = T | ValueOrArray2[]; +>ValueOrArray2 : Symbol(ValueOrArray2, Decl(recursiveTypeReferences1.ts, 80, 47)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 81, 19)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 81, 19)) +>ValueOrArray2 : Symbol(ValueOrArray2, Decl(recursiveTypeReferences1.ts, 80, 47)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 81, 19)) + +declare function foo1(a: ValueOrArray1): T; +>foo1 : Symbol(foo1, Decl(recursiveTypeReferences1.ts, 81, 47)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 83, 22)) +>a : Symbol(a, Decl(recursiveTypeReferences1.ts, 83, 25)) +>ValueOrArray1 : Symbol(ValueOrArray1, Decl(recursiveTypeReferences1.ts, 78, 50)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 83, 22)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 83, 22)) + +declare let ra1: ValueOrArray2; +>ra1 : Symbol(ra1, Decl(recursiveTypeReferences1.ts, 84, 11)) +>ValueOrArray2 : Symbol(ValueOrArray2, Decl(recursiveTypeReferences1.ts, 80, 47)) + +let x1 = foo1(ra1); // Boom! +>x1 : Symbol(x1, Decl(recursiveTypeReferences1.ts, 86, 3)) +>foo1 : Symbol(foo1, Decl(recursiveTypeReferences1.ts, 81, 47)) +>ra1 : Symbol(ra1, Decl(recursiveTypeReferences1.ts, 84, 11)) + +type NumberOrArray1 = T | ValueOrArray1[]; +>NumberOrArray1 : Symbol(NumberOrArray1, Decl(recursiveTypeReferences1.ts, 86, 19)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 88, 20)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 88, 20)) +>ValueOrArray1 : Symbol(ValueOrArray1, Decl(recursiveTypeReferences1.ts, 78, 50)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 88, 20)) + +type NumberOrArray2 = T | ValueOrArray2[]; +>NumberOrArray2 : Symbol(NumberOrArray2, Decl(recursiveTypeReferences1.ts, 88, 48)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 89, 20)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 89, 20)) +>ValueOrArray2 : Symbol(ValueOrArray2, Decl(recursiveTypeReferences1.ts, 80, 47)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 89, 20)) + +declare function foo2(a: ValueOrArray1): T; +>foo2 : Symbol(foo2, Decl(recursiveTypeReferences1.ts, 89, 48)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 91, 22)) +>a : Symbol(a, Decl(recursiveTypeReferences1.ts, 91, 25)) +>ValueOrArray1 : Symbol(ValueOrArray1, Decl(recursiveTypeReferences1.ts, 78, 50)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 91, 22)) +>T : Symbol(T, Decl(recursiveTypeReferences1.ts, 91, 22)) + +declare let ra2: ValueOrArray2; +>ra2 : Symbol(ra2, Decl(recursiveTypeReferences1.ts, 92, 11)) +>ValueOrArray2 : Symbol(ValueOrArray2, Decl(recursiveTypeReferences1.ts, 80, 47)) + +let x2 = foo2(ra2); // Boom! +>x2 : Symbol(x2, Decl(recursiveTypeReferences1.ts, 94, 3)) +>foo2 : Symbol(foo2, Decl(recursiveTypeReferences1.ts, 89, 48)) +>ra2 : Symbol(ra2, Decl(recursiveTypeReferences1.ts, 92, 11)) + +// Repro from #33617 (errors are expected) + +type Tree = [HTMLHeadingElement, Tree][]; +>Tree : Symbol(Tree, Decl(recursiveTypeReferences1.ts, 94, 19)) +>HTMLHeadingElement : Symbol(HTMLHeadingElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>Tree : Symbol(Tree, Decl(recursiveTypeReferences1.ts, 94, 19)) + +function parse(node: Tree, index: number[] = []): HTMLUListElement { +>parse : Symbol(parse, Decl(recursiveTypeReferences1.ts, 98, 41)) +>node : Symbol(node, Decl(recursiveTypeReferences1.ts, 100, 15)) +>Tree : Symbol(Tree, Decl(recursiveTypeReferences1.ts, 94, 19)) +>index : Symbol(index, Decl(recursiveTypeReferences1.ts, 100, 26)) +>HTMLUListElement : Symbol(HTMLUListElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + return html('ul', node.map(([el, children], i) => { +>node.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>node : Symbol(node, Decl(recursiveTypeReferences1.ts, 100, 15)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>el : Symbol(el, Decl(recursiveTypeReferences1.ts, 101, 31)) +>children : Symbol(children, Decl(recursiveTypeReferences1.ts, 101, 34)) +>i : Symbol(i, Decl(recursiveTypeReferences1.ts, 101, 45)) + + const idx = [...index, i + 1]; +>idx : Symbol(idx, Decl(recursiveTypeReferences1.ts, 102, 9)) +>index : Symbol(index, Decl(recursiveTypeReferences1.ts, 100, 26)) +>i : Symbol(i, Decl(recursiveTypeReferences1.ts, 101, 45)) + + return html('li', [ + html('a', { href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent!), +>href : Symbol(href, Decl(recursiveTypeReferences1.ts, 104, 17)) +>el.id : Symbol(Element.id, Decl(lib.dom.d.ts, --, --)) +>el : Symbol(el, Decl(recursiveTypeReferences1.ts, 101, 31)) +>id : Symbol(Element.id, Decl(lib.dom.d.ts, --, --)) +>rel : Symbol(rel, Decl(recursiveTypeReferences1.ts, 104, 36)) +>'data-index' : Symbol('data-index', Decl(recursiveTypeReferences1.ts, 104, 53)) +>idx.join : Symbol(Array.join, Decl(lib.es5.d.ts, --, --)) +>idx : Symbol(idx, Decl(recursiveTypeReferences1.ts, 102, 9)) +>join : Symbol(Array.join, Decl(lib.es5.d.ts, --, --)) +>el.textContent : Symbol(Node.textContent, Decl(lib.dom.d.ts, --, --)) +>el : Symbol(el, Decl(recursiveTypeReferences1.ts, 101, 31)) +>textContent : Symbol(Node.textContent, Decl(lib.dom.d.ts, --, --)) + + children.length > 0 ? parse(children, idx) : frag() +>children.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>children : Symbol(children, Decl(recursiveTypeReferences1.ts, 101, 34)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>parse : Symbol(parse, Decl(recursiveTypeReferences1.ts, 98, 41)) +>children : Symbol(children, Decl(recursiveTypeReferences1.ts, 101, 34)) +>idx : Symbol(idx, Decl(recursiveTypeReferences1.ts, 102, 9)) + + ]); + })); +} + +function cons(hs: HTMLHeadingElement[]): Tree { +>cons : Symbol(cons, Decl(recursiveTypeReferences1.ts, 108, 1)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 110, 14)) +>HTMLHeadingElement : Symbol(HTMLHeadingElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>Tree : Symbol(Tree, Decl(recursiveTypeReferences1.ts, 94, 19)) + + return hs +>hs .reduce((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 : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>hs .reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 110, 14)) + + .reduce((hss, h) => { +>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>HTMLHeadingElement : Symbol(HTMLHeadingElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>hss : Symbol(hss, Decl(recursiveTypeReferences1.ts, 112, 37)) +>h : Symbol(h, Decl(recursiveTypeReferences1.ts, 112, 41)) + + const hs = hss.pop()!; +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 113, 11)) +>hss.pop : Symbol(Array.pop, Decl(lib.es5.d.ts, --, --)) +>hss : Symbol(hss, Decl(recursiveTypeReferences1.ts, 112, 37)) +>pop : Symbol(Array.pop, Decl(lib.es5.d.ts, --, --)) + + return hs.length === 0 || level(h) > level(hs[0]) +>hs.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 113, 11)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>level : Symbol(level, Decl(recursiveTypeReferences1.ts, 123, 1)) +>h : Symbol(h, Decl(recursiveTypeReferences1.ts, 112, 41)) +>level : Symbol(level, Decl(recursiveTypeReferences1.ts, 123, 1)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 113, 11)) + + ? concat(hss, [concat(hs, [h])]) +>hss : Symbol(hss, Decl(recursiveTypeReferences1.ts, 112, 37)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 113, 11)) +>h : Symbol(h, Decl(recursiveTypeReferences1.ts, 112, 41)) + + : concat(hss, [hs, [h]]); +>hss : Symbol(hss, Decl(recursiveTypeReferences1.ts, 112, 37)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 113, 11)) +>h : Symbol(h, Decl(recursiveTypeReferences1.ts, 112, 41)) + + }, [[]]) + .reduce((node, hs) => +>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Tree : Symbol(Tree, Decl(recursiveTypeReferences1.ts, 94, 19)) +>node : Symbol(node, Decl(recursiveTypeReferences1.ts, 118, 19)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 118, 24)) + + hs.length === 0 +>hs.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 118, 24)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + + ? node +>node : Symbol(node, Decl(recursiveTypeReferences1.ts, 118, 19)) + + : concat(node, [[hs.shift()!, cons(hs)]]) +>Tree : Symbol(Tree, Decl(recursiveTypeReferences1.ts, 94, 19)) +>node : Symbol(node, Decl(recursiveTypeReferences1.ts, 118, 19)) +>hs.shift : Symbol(Array.shift, Decl(lib.es5.d.ts, --, --)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 118, 24)) +>shift : Symbol(Array.shift, Decl(lib.es5.d.ts, --, --)) +>cons : Symbol(cons, Decl(recursiveTypeReferences1.ts, 108, 1)) +>hs : Symbol(hs, Decl(recursiveTypeReferences1.ts, 118, 24)) + + , []); +} + +function level(h: HTMLHeadingElement): number { +>level : Symbol(level, Decl(recursiveTypeReferences1.ts, 123, 1)) +>h : Symbol(h, Decl(recursiveTypeReferences1.ts, 125, 15)) +>HTMLHeadingElement : Symbol(HTMLHeadingElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + assert(isFinite(+h.tagName[1])); +>isFinite : Symbol(isFinite, Decl(lib.es5.d.ts, --, --)) +>h.tagName : Symbol(Element.tagName, Decl(lib.dom.d.ts, --, --)) +>h : Symbol(h, Decl(recursiveTypeReferences1.ts, 125, 15)) +>tagName : Symbol(Element.tagName, Decl(lib.dom.d.ts, --, --)) + + return +h.tagName[1]; +>h.tagName : Symbol(Element.tagName, Decl(lib.dom.d.ts, --, --)) +>h : Symbol(h, Decl(recursiveTypeReferences1.ts, 125, 15)) +>tagName : Symbol(Element.tagName, Decl(lib.dom.d.ts, --, --)) +} + diff --git a/tests/baselines/reference/recursiveTypeReferences1.types b/tests/baselines/reference/recursiveTypeReferences1.types index 7f07f2e9c5..0c7b0fb101 100644 --- a/tests/baselines/reference/recursiveTypeReferences1.types +++ b/tests/baselines/reference/recursiveTypeReferences1.types @@ -362,3 +362,249 @@ type T14 = T14[] & { x: string }; type T15 = X extends string ? T15[] : never; >T15 : T15 +type ValueOrArray1 = T | ValueOrArray1[]; +>ValueOrArray1 : ValueOrArray1 + +type ValueOrArray2 = T | ValueOrArray2[]; +>ValueOrArray2 : ValueOrArray2 + +declare function foo1(a: ValueOrArray1): T; +>foo1 : (a: ValueOrArray1) => T +>a : ValueOrArray1 + +declare let ra1: ValueOrArray2; +>ra1 : ValueOrArray2 + +let x1 = foo1(ra1); // Boom! +>x1 : string +>foo1(ra1) : string +>foo1 : (a: ValueOrArray1) => T +>ra1 : ValueOrArray2 + +type NumberOrArray1 = T | ValueOrArray1[]; +>NumberOrArray1 : NumberOrArray1 + +type NumberOrArray2 = T | ValueOrArray2[]; +>NumberOrArray2 : NumberOrArray2 + +declare function foo2(a: ValueOrArray1): T; +>foo2 : (a: ValueOrArray1) => T +>a : ValueOrArray1 + +declare let ra2: ValueOrArray2; +>ra2 : ValueOrArray2 + +let x2 = foo2(ra2); // Boom! +>x2 : string +>foo2(ra2) : string +>foo2 : (a: ValueOrArray1) => T +>ra2 : ValueOrArray2 + +// Repro from #33617 (errors are expected) + +type Tree = [HTMLHeadingElement, Tree][]; +>Tree : Tree + +function parse(node: Tree, index: number[] = []): HTMLUListElement { +>parse : (node: Tree, index?: number[]) => HTMLUListElement +>node : Tree +>index : number[] +>[] : never[] + + return html('ul', node.map(([el, children], i) => { +>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() ]); })) : any +>html : any +>'ul' : "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() ]); }) : any[] +>node.map : (callbackfn: (value: [HTMLHeadingElement, Tree], index: number, array: [HTMLHeadingElement, Tree][]) => U, thisArg?: any) => U[] +>node : Tree +>map : (callbackfn: (value: [HTMLHeadingElement, Tree], index: number, array: [HTMLHeadingElement, Tree][]) => U, thisArg?: any) => U[] +>([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() ]); } : ([el, children]: [HTMLHeadingElement, Tree], i: number) => any +>el : HTMLHeadingElement +>children : Tree +>i : number + + const idx = [...index, i + 1]; +>idx : number[] +>[...index, i + 1] : number[] +>...index : number +>index : number[] +>i + 1 : number +>i : number +>1 : 1 + + return html('li', [ +>html('li', [ html('a', { href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent!), children.length > 0 ? parse(children, idx) : frag() ]) : any +>html : any +>'li' : "li" +>[ html('a', { href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent!), children.length > 0 ? parse(children, idx) : frag() ] : any[] + + html('a', { href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent!), +>html('a', { href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') }, el.textContent!) : any +>html : any +>'a' : "a" +>{ href: `#${el.id}`, rel: 'noopener', 'data-index': idx.join('.') } : { href: string; rel: string; 'data-index': string; } +>href : string +>`#${el.id}` : string +>el.id : string +>el : HTMLHeadingElement +>id : string +>rel : string +>'noopener' : "noopener" +>'data-index' : string +>idx.join('.') : string +>idx.join : (separator?: string | undefined) => string +>idx : number[] +>join : (separator?: string | undefined) => string +>'.' : "." +>el.textContent! : string +>el.textContent : string | null +>el : HTMLHeadingElement +>textContent : string | null + + children.length > 0 ? parse(children, idx) : frag() +>children.length > 0 ? parse(children, idx) : frag() : any +>children.length > 0 : boolean +>children.length : number +>children : Tree +>length : number +>0 : 0 +>parse(children, idx) : HTMLUListElement +>parse : (node: Tree, index?: number[]) => HTMLUListElement +>children : Tree +>idx : number[] +>frag() : any +>frag : any + + ]); + })); +} + +function cons(hs: HTMLHeadingElement[]): Tree { +>cons : (hs: HTMLHeadingElement[]) => Tree +>hs : HTMLHeadingElement[] + + return hs +>hs .reduce((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((node, hs) => hs.length === 0 ? node : concat(node, [[hs.shift()!, cons(hs)]]) , []) : Tree +>hs .reduce((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 : { (callbackfn: (previousValue: HTMLHeadingElement[], currentValue: HTMLHeadingElement[], currentIndex: number, array: HTMLHeadingElement[][]) => HTMLHeadingElement[]): HTMLHeadingElement[]; (callbackfn: (previousValue: HTMLHeadingElement[], currentValue: HTMLHeadingElement[], currentIndex: number, array: HTMLHeadingElement[][]) => HTMLHeadingElement[], initialValue: HTMLHeadingElement[]): HTMLHeadingElement[]; (callbackfn: (previousValue: U, currentValue: HTMLHeadingElement[], currentIndex: number, array: HTMLHeadingElement[][]) => U, initialValue: U): U; } +>hs .reduce((hss, h) => { const hs = hss.pop()!; return hs.length === 0 || level(h) > level(hs[0]) ? concat(hss, [concat(hs, [h])]) : concat(hss, [hs, [h]]); }, [[]]) : HTMLHeadingElement[][] +>hs .reduce : { (callbackfn: (previousValue: HTMLHeadingElement, currentValue: HTMLHeadingElement, currentIndex: number, array: HTMLHeadingElement[]) => HTMLHeadingElement): HTMLHeadingElement; (callbackfn: (previousValue: HTMLHeadingElement, currentValue: HTMLHeadingElement, currentIndex: number, array: HTMLHeadingElement[]) => HTMLHeadingElement, initialValue: HTMLHeadingElement): HTMLHeadingElement; (callbackfn: (previousValue: U, currentValue: HTMLHeadingElement, currentIndex: number, array: HTMLHeadingElement[]) => U, initialValue: U): U; } +>hs : HTMLHeadingElement[] + + .reduce((hss, h) => { +>reduce : { (callbackfn: (previousValue: HTMLHeadingElement, currentValue: HTMLHeadingElement, currentIndex: number, array: HTMLHeadingElement[]) => HTMLHeadingElement): HTMLHeadingElement; (callbackfn: (previousValue: HTMLHeadingElement, currentValue: HTMLHeadingElement, currentIndex: number, array: HTMLHeadingElement[]) => HTMLHeadingElement, initialValue: HTMLHeadingElement): HTMLHeadingElement; (callbackfn: (previousValue: U, currentValue: HTMLHeadingElement, currentIndex: number, array: HTMLHeadingElement[]) => U, initialValue: U): U; } +>(hss, h) => { const hs = hss.pop()!; return hs.length === 0 || level(h) > level(hs[0]) ? concat(hss, [concat(hs, [h])]) : concat(hss, [hs, [h]]); } : (hss: HTMLHeadingElement[][], h: HTMLHeadingElement) => any +>hss : HTMLHeadingElement[][] +>h : HTMLHeadingElement + + const hs = hss.pop()!; +>hs : HTMLHeadingElement[] +>hss.pop()! : HTMLHeadingElement[] +>hss.pop() : HTMLHeadingElement[] | undefined +>hss.pop : () => HTMLHeadingElement[] | undefined +>hss : HTMLHeadingElement[][] +>pop : () => HTMLHeadingElement[] | undefined + + return hs.length === 0 || level(h) > level(hs[0]) +>hs.length === 0 || level(h) > level(hs[0]) ? concat(hss, [concat(hs, [h])]) : concat(hss, [hs, [h]]) : any +>hs.length === 0 || level(h) > level(hs[0]) : boolean +>hs.length === 0 : boolean +>hs.length : number +>hs : HTMLHeadingElement[] +>length : number +>0 : 0 +>level(h) > level(hs[0]) : boolean +>level(h) : number +>level : (h: HTMLHeadingElement) => number +>h : HTMLHeadingElement +>level(hs[0]) : number +>level : (h: HTMLHeadingElement) => number +>hs[0] : HTMLHeadingElement +>hs : HTMLHeadingElement[] +>0 : 0 + + ? concat(hss, [concat(hs, [h])]) +>concat(hss, [concat(hs, [h])]) : any +>concat : any +>hss : HTMLHeadingElement[][] +>[concat(hs, [h])] : any[] +>concat(hs, [h]) : any +>concat : any +>hs : HTMLHeadingElement[] +>[h] : HTMLHeadingElement[] +>h : HTMLHeadingElement + + : concat(hss, [hs, [h]]); +>concat(hss, [hs, [h]]) : any +>concat : any +>hss : HTMLHeadingElement[][] +>[hs, [h]] : HTMLHeadingElement[][] +>hs : HTMLHeadingElement[] +>[h] : HTMLHeadingElement[] +>h : HTMLHeadingElement + + }, [[]]) +>[[]] : never[][] +>[] : never[] + + .reduce((node, hs) => +>reduce : { (callbackfn: (previousValue: HTMLHeadingElement[], currentValue: HTMLHeadingElement[], currentIndex: number, array: HTMLHeadingElement[][]) => HTMLHeadingElement[]): HTMLHeadingElement[]; (callbackfn: (previousValue: HTMLHeadingElement[], currentValue: HTMLHeadingElement[], currentIndex: number, array: HTMLHeadingElement[][]) => HTMLHeadingElement[], initialValue: HTMLHeadingElement[]): HTMLHeadingElement[]; (callbackfn: (previousValue: U, currentValue: HTMLHeadingElement[], currentIndex: number, array: HTMLHeadingElement[][]) => U, initialValue: U): U; } +>(node, hs) => hs.length === 0 ? node : concat(node, [[hs.shift()!, cons(hs)]]) : (node: Tree, hs: HTMLHeadingElement[]) => any +>node : Tree +>hs : HTMLHeadingElement[] + + hs.length === 0 +>hs.length === 0 ? node : concat(node, [[hs.shift()!, cons(hs)]]) : any +>hs.length === 0 : boolean +>hs.length : number +>hs : HTMLHeadingElement[] +>length : number +>0 : 0 + + ? node +>node : Tree + + : concat(node, [[hs.shift()!, cons(hs)]]) +>concat(node, [[hs.shift()!, cons(hs)]]) : any +>concat : any +>node : Tree +>[[hs.shift()!, cons(hs)]] : (HTMLHeadingElement | Tree)[][] +>[hs.shift()!, cons(hs)] : (HTMLHeadingElement | Tree)[] +>hs.shift()! : HTMLHeadingElement +>hs.shift() : HTMLHeadingElement | undefined +>hs.shift : () => HTMLHeadingElement | undefined +>hs : HTMLHeadingElement[] +>shift : () => HTMLHeadingElement | undefined +>cons(hs) : Tree +>cons : (hs: HTMLHeadingElement[]) => Tree +>hs : HTMLHeadingElement[] + + , []); +>[] : never[] +} + +function level(h: HTMLHeadingElement): number { +>level : (h: HTMLHeadingElement) => number +>h : HTMLHeadingElement + + assert(isFinite(+h.tagName[1])); +>assert(isFinite(+h.tagName[1])) : any +>assert : any +>isFinite(+h.tagName[1]) : boolean +>isFinite : (number: number) => boolean +>+h.tagName[1] : number +>h.tagName[1] : string +>h.tagName : string +>h : HTMLHeadingElement +>tagName : string +>1 : 1 + + return +h.tagName[1]; +>+h.tagName[1] : number +>h.tagName[1] : string +>h.tagName : string +>h : HTMLHeadingElement +>tagName : string +>1 : 1 +} + diff --git a/tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts b/tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts index 16e2328496..c4d7cbc169 100644 --- a/tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts +++ b/tests/cases/conformance/types/typeRelationships/recursiveTypes/recursiveTypeReferences1.ts @@ -80,3 +80,53 @@ type T12 = (T12)[]; type T13 = T13[] | string; type T14 = T14[] & { x: string }; type T15 = X extends string ? T15[] : never; + +type ValueOrArray1 = T | ValueOrArray1[]; +type ValueOrArray2 = T | ValueOrArray2[]; + +declare function foo1(a: ValueOrArray1): T; +declare let ra1: ValueOrArray2; + +let x1 = foo1(ra1); // Boom! + +type NumberOrArray1 = T | ValueOrArray1[]; +type NumberOrArray2 = T | ValueOrArray2[]; + +declare function foo2(a: ValueOrArray1): T; +declare let ra2: ValueOrArray2; + +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((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((node, hs) => + hs.length === 0 + ? node + : concat(node, [[hs.shift()!, cons(hs)]]) + , []); +} + +function level(h: HTMLHeadingElement): number { + assert(isFinite(+h.tagName[1])); + return +h.tagName[1]; +}