From 3f004853155e6afef70ee7ccb3b221eb8e736e1e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 10 Feb 2023 12:08:49 -0800 Subject: [PATCH 1/6] Fix contextual typing for post-spread array literal elements --- src/compiler/checker.ts | 13 +++++++++---- src/compiler/types.ts | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 93f559c1faa79..c9c85ee019995 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28838,9 +28838,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // type of T. function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined { return arrayContextualType && ( - getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) - || mapType( - arrayContextualType, + index >= 0 && getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) || + getIndexTypeOfType(arrayContextualType, numberType) || + mapType(arrayContextualType, t => getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false), /*noReductions*/ true)); } @@ -29072,7 +29072,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ArrayLiteralExpression: { const arrayLiteral = parent as ArrayLiteralExpression; const type = getApparentTypeOfContextualType(arrayLiteral, contextFlags); - return getContextualTypeForElementExpression(type, indexOfNode(arrayLiteral.elements, node)); + // The index of an array literal element doesn't necessarily line up with the index of the corresponding + // element in a contextual tuple type when there are preceding spread elements in the array literal. For + // this reason we only pass indices for elements that precede the first spread element. + const spreadIndex = getNodeLinks(arrayLiteral).firstSpreadIndex ??= findIndex(arrayLiteral.elements, isSpreadElement); + const elementIndex = indexOfNode(arrayLiteral.elements, node); + return getContextualTypeForElementExpression(type, spreadIndex < 0 || elementIndex < spreadIndex ? elementIndex : -1); } case SyntaxKind.ConditionalExpression: return getContextualTypeForConditionalOperand(node, contextFlags); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 71cc346bef0a3..0748d7fbb5380 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5997,6 +5997,7 @@ export interface NodeLinks { declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. serializedTypes?: Map; // Collection of types serialized at this location decoratorSignature?: Signature; // Signature for decorator as if invoked by the runtime. + firstSpreadIndex?: number; // Index of first spread element in array literal (-1 for none) } /** @internal */ From 0fab031916d9425b4bc18d2fb61b9b32066cfb33 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 14 Feb 2023 16:15:52 -0800 Subject: [PATCH 2/6] No union reduction for contextual types originating in tuples --- src/compiler/checker.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c9c85ee019995..ab4df75865135 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28839,9 +28839,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined { return arrayContextualType && ( index >= 0 && getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) || - getIndexTypeOfType(arrayContextualType, numberType) || - mapType(arrayContextualType, - t => getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false), + mapType(arrayContextualType, t => + isTupleType(t) ? + getUnionType(sameMap(getTypeArguments(t), (e, i) => t.target.elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessType(e, numberType) : e), UnionReduction.None) : + getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false), /*noReductions*/ true)); } From b043afc818afb4df05adbedee42aba13d9874183 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 14 Feb 2023 16:19:25 -0800 Subject: [PATCH 3/6] Add tests --- .../spreadsAndContextualTupleTypes.symbols | 115 ++++++++++ .../spreadsAndContextualTupleTypes.types | 200 ++++++++++++++++++ .../spreadsAndContextualTupleTypes.ts | 35 +++ 3 files changed, 350 insertions(+) create mode 100644 tests/baselines/reference/spreadsAndContextualTupleTypes.symbols create mode 100644 tests/baselines/reference/spreadsAndContextualTupleTypes.types create mode 100644 tests/cases/compiler/spreadsAndContextualTupleTypes.ts diff --git a/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols b/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols new file mode 100644 index 0000000000000..c44949a9fcd5c --- /dev/null +++ b/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols @@ -0,0 +1,115 @@ +=== tests/cases/compiler/spreadsAndContextualTupleTypes.ts === +declare function fx4(x: T): T; +>fx4 : Symbol(fx4, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 0, 21)) +>x : Symbol(x, Decl(spreadsAndContextualTupleTypes.ts, 0, 68)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 0, 21)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 0, 21)) + +const t3 = ['x', 'y', 'z'] as const; +>t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 2, 5)) +>const : Symbol(const) + +fx4(['x', 'y', 'z', 'a']); +>fx4 : Symbol(fx4, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) + +fx4([...t3, 'a']); +>fx4 : Symbol(fx4, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) +>t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 2, 5)) + +// Repro from #52684 + +const staticPath1Level = ["home"] as const; +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) +>const : Symbol(const) + +const staticPath2Level = ["home", "user"] as const; +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) +>const : Symbol(const) + +const staticPath3Level = ["home", "user", "downloads"] as const; +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) +>const : Symbol(const) + +const randomID = 'id' as string; +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +declare function foo(path: T): T; +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 15, 21)) +>path : Symbol(path, Decl(spreadsAndContextualTupleTypes.ts, 15, 30)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 15, 21)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 15, 21)) + +const a1 = foo([...staticPath1Level, randomID, 'doc.pdf']); +>a1 : Symbol(a1, Decl(spreadsAndContextualTupleTypes.ts, 17, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const a2 = foo([...staticPath2Level, randomID, 'doc.pdf']); +>a2 : Symbol(a2, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const a3 = foo([...staticPath3Level, randomID, 'doc.pdf']); +>a3 : Symbol(a3, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const b1 = foo([...staticPath1Level, randomID, 'folder', 'doc.pdf']); +>b1 : Symbol(b1, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const b2 = foo([...staticPath2Level, randomID, 'folder', 'doc.pdf']); +>b2 : Symbol(b2, Decl(spreadsAndContextualTupleTypes.ts, 22, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const b3 = foo([...staticPath3Level, randomID, 'folder', 'doc.pdf']); +>b3 : Symbol(b3, Decl(spreadsAndContextualTupleTypes.ts, 23, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const c1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +>c1 : Symbol(c1, Decl(spreadsAndContextualTupleTypes.ts, 25, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const c2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +>c2 : Symbol(c2, Decl(spreadsAndContextualTupleTypes.ts, 26, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const c3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +>c3 : Symbol(c3, Decl(spreadsAndContextualTupleTypes.ts, 27, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const d1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +>d1 : Symbol(d1, Decl(spreadsAndContextualTupleTypes.ts, 29, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const d2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +>d2 : Symbol(d2, Decl(spreadsAndContextualTupleTypes.ts, 30, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + +const d3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +>d3 : Symbol(d3, Decl(spreadsAndContextualTupleTypes.ts, 31, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) + diff --git a/tests/baselines/reference/spreadsAndContextualTupleTypes.types b/tests/baselines/reference/spreadsAndContextualTupleTypes.types new file mode 100644 index 0000000000000..5cc0995adb65d --- /dev/null +++ b/tests/baselines/reference/spreadsAndContextualTupleTypes.types @@ -0,0 +1,200 @@ +=== tests/cases/compiler/spreadsAndContextualTupleTypes.ts === +declare function fx4(x: T): T; +>fx4 : (x: T) => T +>x : T + +const t3 = ['x', 'y', 'z'] as const; +>t3 : readonly ["x", "y", "z"] +>['x', 'y', 'z'] as const : readonly ["x", "y", "z"] +>['x', 'y', 'z'] : readonly ["x", "y", "z"] +>'x' : "x" +>'y' : "y" +>'z' : "z" + +fx4(['x', 'y', 'z', 'a']); +>fx4(['x', 'y', 'z', 'a']) : [string, string, string, "a"] +>fx4 : (x: T) => T +>['x', 'y', 'z', 'a'] : [string, string, string, "a"] +>'x' : "x" +>'y' : "y" +>'z' : "z" +>'a' : "a" + +fx4([...t3, 'a']); +>fx4([...t3, 'a']) : ["x", "y", "z", "a"] +>fx4 : (x: T) => T +>[...t3, 'a'] : ["x", "y", "z", "a"] +>...t3 : "x" | "y" | "z" +>t3 : readonly ["x", "y", "z"] +>'a' : "a" + +// Repro from #52684 + +const staticPath1Level = ["home"] as const; +>staticPath1Level : readonly ["home"] +>["home"] as const : readonly ["home"] +>["home"] : readonly ["home"] +>"home" : "home" + +const staticPath2Level = ["home", "user"] as const; +>staticPath2Level : readonly ["home", "user"] +>["home", "user"] as const : readonly ["home", "user"] +>["home", "user"] : readonly ["home", "user"] +>"home" : "home" +>"user" : "user" + +const staticPath3Level = ["home", "user", "downloads"] as const; +>staticPath3Level : readonly ["home", "user", "downloads"] +>["home", "user", "downloads"] as const : readonly ["home", "user", "downloads"] +>["home", "user", "downloads"] : readonly ["home", "user", "downloads"] +>"home" : "home" +>"user" : "user" +>"downloads" : "downloads" + +const randomID = 'id' as string; +>randomID : string +>'id' as string : string +>'id' : "id" + +declare function foo(path: T): T; +>foo : (path: T) => T +>path : T + +const a1 = foo([...staticPath1Level, randomID, 'doc.pdf']); +>a1 : readonly ["home", string, "doc.pdf"] +>foo([...staticPath1Level, randomID, 'doc.pdf']) : readonly ["home", string, "doc.pdf"] +>foo : (path: T) => T +>[...staticPath1Level, randomID, 'doc.pdf'] : ["home", string, "doc.pdf"] +>...staticPath1Level : "home" +>staticPath1Level : readonly ["home"] +>randomID : string +>'doc.pdf' : "doc.pdf" + +const a2 = foo([...staticPath2Level, randomID, 'doc.pdf']); +>a2 : readonly ["home", "user", string, "doc.pdf"] +>foo([...staticPath2Level, randomID, 'doc.pdf']) : readonly ["home", "user", string, "doc.pdf"] +>foo : (path: T) => T +>[...staticPath2Level, randomID, 'doc.pdf'] : ["home", "user", string, "doc.pdf"] +>...staticPath2Level : "home" | "user" +>staticPath2Level : readonly ["home", "user"] +>randomID : string +>'doc.pdf' : "doc.pdf" + +const a3 = foo([...staticPath3Level, randomID, 'doc.pdf']); +>a3 : readonly ["home", "user", "downloads", string, "doc.pdf"] +>foo([...staticPath3Level, randomID, 'doc.pdf']) : readonly ["home", "user", "downloads", string, "doc.pdf"] +>foo : (path: T) => T +>[...staticPath3Level, randomID, 'doc.pdf'] : ["home", "user", "downloads", string, "doc.pdf"] +>...staticPath3Level : "home" | "user" | "downloads" +>staticPath3Level : readonly ["home", "user", "downloads"] +>randomID : string +>'doc.pdf' : "doc.pdf" + +const b1 = foo([...staticPath1Level, randomID, 'folder', 'doc.pdf']); +>b1 : readonly ["home", string, "folder", "doc.pdf"] +>foo([...staticPath1Level, randomID, 'folder', 'doc.pdf']) : readonly ["home", string, "folder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath1Level, randomID, 'folder', 'doc.pdf'] : ["home", string, "folder", "doc.pdf"] +>...staticPath1Level : "home" +>staticPath1Level : readonly ["home"] +>randomID : string +>'folder' : "folder" +>'doc.pdf' : "doc.pdf" + +const b2 = foo([...staticPath2Level, randomID, 'folder', 'doc.pdf']); +>b2 : readonly ["home", "user", string, "folder", "doc.pdf"] +>foo([...staticPath2Level, randomID, 'folder', 'doc.pdf']) : readonly ["home", "user", string, "folder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath2Level, randomID, 'folder', 'doc.pdf'] : ["home", "user", string, "folder", "doc.pdf"] +>...staticPath2Level : "home" | "user" +>staticPath2Level : readonly ["home", "user"] +>randomID : string +>'folder' : "folder" +>'doc.pdf' : "doc.pdf" + +const b3 = foo([...staticPath3Level, randomID, 'folder', 'doc.pdf']); +>b3 : readonly ["home", "user", "downloads", string, "folder", "doc.pdf"] +>foo([...staticPath3Level, randomID, 'folder', 'doc.pdf']) : readonly ["home", "user", "downloads", string, "folder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath3Level, randomID, 'folder', 'doc.pdf'] : ["home", "user", "downloads", string, "folder", "doc.pdf"] +>...staticPath3Level : "home" | "user" | "downloads" +>staticPath3Level : readonly ["home", "user", "downloads"] +>randomID : string +>'folder' : "folder" +>'doc.pdf' : "doc.pdf" + +const c1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +>c1 : readonly ["home", string, "folder", "subfolder", "doc.pdf"] +>foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'doc.pdf']) : readonly ["home", string, "folder", "subfolder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath1Level, randomID, 'folder', 'subfolder', 'doc.pdf'] : ["home", string, "folder", "subfolder", "doc.pdf"] +>...staticPath1Level : "home" +>staticPath1Level : readonly ["home"] +>randomID : string +>'folder' : "folder" +>'subfolder' : "subfolder" +>'doc.pdf' : "doc.pdf" + +const c2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +>c2 : readonly ["home", "user", string, "folder", "subfolder", "doc.pdf"] +>foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'doc.pdf']) : readonly ["home", "user", string, "folder", "subfolder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath2Level, randomID, 'folder', 'subfolder', 'doc.pdf'] : ["home", "user", string, "folder", "subfolder", "doc.pdf"] +>...staticPath2Level : "home" | "user" +>staticPath2Level : readonly ["home", "user"] +>randomID : string +>'folder' : "folder" +>'subfolder' : "subfolder" +>'doc.pdf' : "doc.pdf" + +const c3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +>c3 : readonly ["home", "user", "downloads", string, "folder", "subfolder", "doc.pdf"] +>foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'doc.pdf']) : readonly ["home", "user", "downloads", string, "folder", "subfolder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath3Level, randomID, 'folder', 'subfolder', 'doc.pdf'] : ["home", "user", "downloads", string, "folder", "subfolder", "doc.pdf"] +>...staticPath3Level : "home" | "user" | "downloads" +>staticPath3Level : readonly ["home", "user", "downloads"] +>randomID : string +>'folder' : "folder" +>'subfolder' : "subfolder" +>'doc.pdf' : "doc.pdf" + +const d1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +>d1 : readonly ["home", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']) : readonly ["home", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath1Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf'] : ["home", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>...staticPath1Level : "home" +>staticPath1Level : readonly ["home"] +>randomID : string +>'folder' : "folder" +>'subfolder' : "subfolder" +>'another-subfolder' : "another-subfolder" +>'doc.pdf' : "doc.pdf" + +const d2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +>d2 : readonly ["home", "user", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']) : readonly ["home", "user", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath2Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf'] : ["home", "user", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>...staticPath2Level : "home" | "user" +>staticPath2Level : readonly ["home", "user"] +>randomID : string +>'folder' : "folder" +>'subfolder' : "subfolder" +>'another-subfolder' : "another-subfolder" +>'doc.pdf' : "doc.pdf" + +const d3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +>d3 : readonly ["home", "user", "downloads", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']) : readonly ["home", "user", "downloads", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>foo : (path: T) => T +>[...staticPath3Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf'] : ["home", "user", "downloads", string, "folder", "subfolder", "another-subfolder", "doc.pdf"] +>...staticPath3Level : "home" | "user" | "downloads" +>staticPath3Level : readonly ["home", "user", "downloads"] +>randomID : string +>'folder' : "folder" +>'subfolder' : "subfolder" +>'another-subfolder' : "another-subfolder" +>'doc.pdf' : "doc.pdf" + diff --git a/tests/cases/compiler/spreadsAndContextualTupleTypes.ts b/tests/cases/compiler/spreadsAndContextualTupleTypes.ts new file mode 100644 index 0000000000000..08de100202ea0 --- /dev/null +++ b/tests/cases/compiler/spreadsAndContextualTupleTypes.ts @@ -0,0 +1,35 @@ +// @strict: true +// @noEmit: true + +declare function fx4(x: T): T; + +const t3 = ['x', 'y', 'z'] as const; + +fx4(['x', 'y', 'z', 'a']); +fx4([...t3, 'a']); + +// Repro from #52684 + +const staticPath1Level = ["home"] as const; +const staticPath2Level = ["home", "user"] as const; +const staticPath3Level = ["home", "user", "downloads"] as const; + +const randomID = 'id' as string; + +declare function foo(path: T): T; + +const a1 = foo([...staticPath1Level, randomID, 'doc.pdf']); +const a2 = foo([...staticPath2Level, randomID, 'doc.pdf']); +const a3 = foo([...staticPath3Level, randomID, 'doc.pdf']); + +const b1 = foo([...staticPath1Level, randomID, 'folder', 'doc.pdf']); +const b2 = foo([...staticPath2Level, randomID, 'folder', 'doc.pdf']); +const b3 = foo([...staticPath3Level, randomID, 'folder', 'doc.pdf']); + +const c1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +const c2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'doc.pdf']); +const c3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'doc.pdf']); + +const d1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +const d2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); +const d3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); From 9588571e2b4e7d4c245212743ce7529c442331f9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 15 Feb 2023 09:26:13 -0800 Subject: [PATCH 4/6] No union reduction of contextual tuple rest type --- src/compiler/checker.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f0a1f83617488..755442a8e3373 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23074,7 +23074,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return restType && createArrayType(restType); } - function getElementTypeOfSliceOfTupleType(type: TupleTypeReference, index: number, endSkipCount = 0, writing = false) { + function getElementTypeOfSliceOfTupleType(type: TupleTypeReference, index: number, endSkipCount = 0, writing = false, noReductions = false) { const length = getTypeReferenceArity(type) - endSkipCount; if (index < length) { const typeArguments = getTypeArguments(type); @@ -23083,7 +23083,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const t = typeArguments[i]; elementTypes.push(type.target.elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessType(t, numberType) : t); } - return writing ? getIntersectionType(elementTypes) : getUnionType(elementTypes); + return writing ? getIntersectionType(elementTypes) : getUnionType(elementTypes, noReductions ? UnionReduction.None : UnionReduction.Literal); } return undefined; } @@ -28829,9 +28829,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (prop) { return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop); } - if (isTupleType(t)) { - const restType = getRestTypeOfTupleType(t); - if (restType && isNumericLiteralName(name) && +name >= 0) { + if (isTupleType(t) && isNumericLiteralName(name) && +name >= 0) { + const restType = getElementTypeOfSliceOfTupleType(t, t.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true); + if (restType) { return restType; } } @@ -28886,7 +28886,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { index >= 0 && getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) || mapType(arrayContextualType, t => isTupleType(t) ? - getUnionType(sameMap(getTypeArguments(t), (e, i) => t.target.elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessType(e, numberType) : e), UnionReduction.None) : + getElementTypeOfSliceOfTupleType(t, 0, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true) : getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false), /*noReductions*/ true)); } From 6e84767bb3e31f17650251f8d00aa61cfdeb9764 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 15 Feb 2023 09:26:35 -0800 Subject: [PATCH 5/6] Accept new baselines --- tests/baselines/reference/variadicTuples2.types | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/variadicTuples2.types b/tests/baselines/reference/variadicTuples2.types index d21c09a69eb8f..0fd699df34f05 100644 --- a/tests/baselines/reference/variadicTuples2.types +++ b/tests/baselines/reference/variadicTuples2.types @@ -465,7 +465,7 @@ fn1([1, 'abc']); // [number, string] fn1([1, 'abc', true]); // [string, boolean] >fn1([1, 'abc', true]) : [string, boolean] >fn1 : (t: [...unknown[], T, U]) => [T, U] ->[1, 'abc', true] : [number, string, boolean] +>[1, 'abc', true] : [number, string, true] >1 : 1 >'abc' : "abc" >true : true @@ -495,7 +495,7 @@ fn2([1, 'abc']); // [number, string] fn2([1, 'abc', true]); // [number, boolean] >fn2([1, 'abc', true]) : [number, boolean] >fn2 : (t: [T, ...unknown[], U]) => [T, U] ->[1, 'abc', true] : [number, string, boolean] +>[1, 'abc', true] : [number, string, true] >1 : 1 >'abc' : "abc" >true : true From 2631e2f7f20b47a9deb01338d7300d20a6f5d835 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 15 Feb 2023 09:26:43 -0800 Subject: [PATCH 6/6] Add more tests --- .../spreadsAndContextualTupleTypes.symbols | 154 ++++++++++-------- .../spreadsAndContextualTupleTypes.types | 55 ++++++- .../spreadsAndContextualTupleTypes.ts | 14 +- 3 files changed, 147 insertions(+), 76 deletions(-) diff --git a/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols b/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols index c44949a9fcd5c..d1384d93d5843 100644 --- a/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols +++ b/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols @@ -1,115 +1,139 @@ === tests/cases/compiler/spreadsAndContextualTupleTypes.ts === -declare function fx4(x: T): T; ->fx4 : Symbol(fx4, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) +declare function fx1(x: T): T; +>fx1 : Symbol(fx1, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) >T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 0, 21)) >x : Symbol(x, Decl(spreadsAndContextualTupleTypes.ts, 0, 68)) >T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 0, 21)) >T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 0, 21)) +declare function fx2(x: T): T; +>fx2 : Symbol(fx2, Decl(spreadsAndContextualTupleTypes.ts, 0, 77)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 1, 21)) +>x : Symbol(x, Decl(spreadsAndContextualTupleTypes.ts, 1, 57)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 1, 21)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 1, 21)) + const t3 = ['x', 'y', 'z'] as const; ->t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 2, 5)) +>t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 3, 5)) >const : Symbol(const) -fx4(['x', 'y', 'z', 'a']); ->fx4 : Symbol(fx4, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) +fx1(['x', 'y', 'z', 'a']); +>fx1 : Symbol(fx1, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) + +fx1([...t3, 'a']); +>fx1 : Symbol(fx1, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) +>t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 3, 5)) + +fx2(['x', 'y', 'z', 'a']); +>fx2 : Symbol(fx2, Decl(spreadsAndContextualTupleTypes.ts, 0, 77)) + +fx2([...t3, 'a']); +>fx2 : Symbol(fx2, Decl(spreadsAndContextualTupleTypes.ts, 0, 77)) +>t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 3, 5)) + +const x1: [...string[], '!'] = ['!']; +>x1 : Symbol(x1, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) + +const x2: [...string[], '!'] = ['a', '!']; +>x2 : Symbol(x2, Decl(spreadsAndContextualTupleTypes.ts, 12, 5)) -fx4([...t3, 'a']); ->fx4 : Symbol(fx4, Decl(spreadsAndContextualTupleTypes.ts, 0, 0)) ->t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 2, 5)) +const x3: [...string[], '!'] = [...t3, '!']; +>x3 : Symbol(x3, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>t3 : Symbol(t3, Decl(spreadsAndContextualTupleTypes.ts, 3, 5)) // Repro from #52684 const staticPath1Level = ["home"] as const; ->staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 17, 5)) >const : Symbol(const) const staticPath2Level = ["home", "user"] as const; ->staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) >const : Symbol(const) const staticPath3Level = ["home", "user", "downloads"] as const; ->staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) >const : Symbol(const) const randomID = 'id' as string; ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) declare function foo(path: T): T; ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 15, 21)) ->path : Symbol(path, Decl(spreadsAndContextualTupleTypes.ts, 15, 30)) ->T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 15, 21)) ->T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 15, 21)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 23, 21)) +>path : Symbol(path, Decl(spreadsAndContextualTupleTypes.ts, 23, 30)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 23, 21)) +>T : Symbol(T, Decl(spreadsAndContextualTupleTypes.ts, 23, 21)) const a1 = foo([...staticPath1Level, randomID, 'doc.pdf']); ->a1 : Symbol(a1, Decl(spreadsAndContextualTupleTypes.ts, 17, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>a1 : Symbol(a1, Decl(spreadsAndContextualTupleTypes.ts, 25, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 17, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const a2 = foo([...staticPath2Level, randomID, 'doc.pdf']); ->a2 : Symbol(a2, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>a2 : Symbol(a2, Decl(spreadsAndContextualTupleTypes.ts, 26, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const a3 = foo([...staticPath3Level, randomID, 'doc.pdf']); ->a3 : Symbol(a3, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>a3 : Symbol(a3, Decl(spreadsAndContextualTupleTypes.ts, 27, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const b1 = foo([...staticPath1Level, randomID, 'folder', 'doc.pdf']); ->b1 : Symbol(b1, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>b1 : Symbol(b1, Decl(spreadsAndContextualTupleTypes.ts, 29, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 17, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const b2 = foo([...staticPath2Level, randomID, 'folder', 'doc.pdf']); ->b2 : Symbol(b2, Decl(spreadsAndContextualTupleTypes.ts, 22, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>b2 : Symbol(b2, Decl(spreadsAndContextualTupleTypes.ts, 30, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const b3 = foo([...staticPath3Level, randomID, 'folder', 'doc.pdf']); ->b3 : Symbol(b3, Decl(spreadsAndContextualTupleTypes.ts, 23, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>b3 : Symbol(b3, Decl(spreadsAndContextualTupleTypes.ts, 31, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const c1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'doc.pdf']); ->c1 : Symbol(c1, Decl(spreadsAndContextualTupleTypes.ts, 25, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>c1 : Symbol(c1, Decl(spreadsAndContextualTupleTypes.ts, 33, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 17, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const c2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'doc.pdf']); ->c2 : Symbol(c2, Decl(spreadsAndContextualTupleTypes.ts, 26, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>c2 : Symbol(c2, Decl(spreadsAndContextualTupleTypes.ts, 34, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const c3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'doc.pdf']); ->c3 : Symbol(c3, Decl(spreadsAndContextualTupleTypes.ts, 27, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>c3 : Symbol(c3, Decl(spreadsAndContextualTupleTypes.ts, 35, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const d1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); ->d1 : Symbol(d1, Decl(spreadsAndContextualTupleTypes.ts, 29, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 9, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>d1 : Symbol(d1, Decl(spreadsAndContextualTupleTypes.ts, 37, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath1Level : Symbol(staticPath1Level, Decl(spreadsAndContextualTupleTypes.ts, 17, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const d2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); ->d2 : Symbol(d2, Decl(spreadsAndContextualTupleTypes.ts, 30, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 10, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>d2 : Symbol(d2, Decl(spreadsAndContextualTupleTypes.ts, 38, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) const d3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']); ->d3 : Symbol(d3, Decl(spreadsAndContextualTupleTypes.ts, 31, 5)) ->foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 13, 32)) ->staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 11, 5)) ->randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 13, 5)) +>d3 : Symbol(d3, Decl(spreadsAndContextualTupleTypes.ts, 39, 5)) +>foo : Symbol(foo, Decl(spreadsAndContextualTupleTypes.ts, 21, 32)) +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) diff --git a/tests/baselines/reference/spreadsAndContextualTupleTypes.types b/tests/baselines/reference/spreadsAndContextualTupleTypes.types index 5cc0995adb65d..5224eb8debf15 100644 --- a/tests/baselines/reference/spreadsAndContextualTupleTypes.types +++ b/tests/baselines/reference/spreadsAndContextualTupleTypes.types @@ -1,6 +1,10 @@ === tests/cases/compiler/spreadsAndContextualTupleTypes.ts === -declare function fx4(x: T): T; ->fx4 : (x: T) => T +declare function fx1(x: T): T; +>fx1 : (x: T) => T +>x : T + +declare function fx2(x: T): T; +>fx2 : (x: T) => T >x : T const t3 = ['x', 'y', 'z'] as const; @@ -11,23 +15,58 @@ const t3 = ['x', 'y', 'z'] as const; >'y' : "y" >'z' : "z" -fx4(['x', 'y', 'z', 'a']); ->fx4(['x', 'y', 'z', 'a']) : [string, string, string, "a"] ->fx4 : (x: T) => T +fx1(['x', 'y', 'z', 'a']); +>fx1(['x', 'y', 'z', 'a']) : [string, string, string, "a"] +>fx1 : (x: T) => T >['x', 'y', 'z', 'a'] : [string, string, string, "a"] >'x' : "x" >'y' : "y" >'z' : "z" >'a' : "a" -fx4([...t3, 'a']); ->fx4([...t3, 'a']) : ["x", "y", "z", "a"] ->fx4 : (x: T) => T +fx1([...t3, 'a']); +>fx1([...t3, 'a']) : ["x", "y", "z", "a"] +>fx1 : (x: T) => T +>[...t3, 'a'] : ["x", "y", "z", "a"] +>...t3 : "x" | "y" | "z" +>t3 : readonly ["x", "y", "z"] +>'a' : "a" + +fx2(['x', 'y', 'z', 'a']); +>fx2(['x', 'y', 'z', 'a']) : ["x", "y", "z", "a"] +>fx2 : (x: T) => T +>['x', 'y', 'z', 'a'] : ["x", "y", "z", "a"] +>'x' : "x" +>'y' : "y" +>'z' : "z" +>'a' : "a" + +fx2([...t3, 'a']); +>fx2([...t3, 'a']) : ["x", "y", "z", "a"] +>fx2 : (x: T) => T >[...t3, 'a'] : ["x", "y", "z", "a"] >...t3 : "x" | "y" | "z" >t3 : readonly ["x", "y", "z"] >'a' : "a" +const x1: [...string[], '!'] = ['!']; +>x1 : [...string[], "!"] +>['!'] : ["!"] +>'!' : "!" + +const x2: [...string[], '!'] = ['a', '!']; +>x2 : [...string[], "!"] +>['a', '!'] : ["a", "!"] +>'a' : "a" +>'!' : "!" + +const x3: [...string[], '!'] = [...t3, '!']; +>x3 : [...string[], "!"] +>[...t3, '!'] : ["x", "y", "z", "!"] +>...t3 : "x" | "y" | "z" +>t3 : readonly ["x", "y", "z"] +>'!' : "!" + // Repro from #52684 const staticPath1Level = ["home"] as const; diff --git a/tests/cases/compiler/spreadsAndContextualTupleTypes.ts b/tests/cases/compiler/spreadsAndContextualTupleTypes.ts index 08de100202ea0..dcde8f52464f9 100644 --- a/tests/cases/compiler/spreadsAndContextualTupleTypes.ts +++ b/tests/cases/compiler/spreadsAndContextualTupleTypes.ts @@ -1,12 +1,20 @@ // @strict: true // @noEmit: true -declare function fx4(x: T): T; +declare function fx1(x: T): T; +declare function fx2(x: T): T; const t3 = ['x', 'y', 'z'] as const; -fx4(['x', 'y', 'z', 'a']); -fx4([...t3, 'a']); +fx1(['x', 'y', 'z', 'a']); +fx1([...t3, 'a']); + +fx2(['x', 'y', 'z', 'a']); +fx2([...t3, 'a']); + +const x1: [...string[], '!'] = ['!']; +const x2: [...string[], '!'] = ['a', '!']; +const x3: [...string[], '!'] = [...t3, '!']; // Repro from #52684