diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1cd31d70417ab..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; } } @@ -28883,10 +28883,11 @@ 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, - t => getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false), + index >= 0 && getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) || + mapType(arrayContextualType, t => + isTupleType(t) ? + getElementTypeOfSliceOfTupleType(t, 0, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true) : + getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false), /*noReductions*/ true)); } @@ -29118,7 +29119,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 94ae76b448b98..7a3440f25506d 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) parameterInitializerContainsUndefined?: boolean; // True if this is a parameter declaration whose type annotation contains "undefined". } diff --git a/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols b/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols new file mode 100644 index 0000000000000..d1384d93d5843 --- /dev/null +++ b/tests/baselines/reference/spreadsAndContextualTupleTypes.symbols @@ -0,0 +1,139 @@ +=== tests/cases/compiler/spreadsAndContextualTupleTypes.ts === +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, 3, 5)) +>const : Symbol(const) + +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)) + +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, 17, 5)) +>const : Symbol(const) + +const staticPath2Level = ["home", "user"] as const; +>staticPath2Level : Symbol(staticPath2Level, Decl(spreadsAndContextualTupleTypes.ts, 18, 5)) +>const : Symbol(const) + +const staticPath3Level = ["home", "user", "downloads"] as const; +>staticPath3Level : Symbol(staticPath3Level, Decl(spreadsAndContextualTupleTypes.ts, 19, 5)) +>const : Symbol(const) + +const randomID = 'id' as string; +>randomID : Symbol(randomID, Decl(spreadsAndContextualTupleTypes.ts, 21, 5)) + +declare function foo(path: T): T; +>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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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 new file mode 100644 index 0000000000000..5224eb8debf15 --- /dev/null +++ b/tests/baselines/reference/spreadsAndContextualTupleTypes.types @@ -0,0 +1,239 @@ +=== tests/cases/compiler/spreadsAndContextualTupleTypes.ts === +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; +>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" + +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" + +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; +>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/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 diff --git a/tests/cases/compiler/spreadsAndContextualTupleTypes.ts b/tests/cases/compiler/spreadsAndContextualTupleTypes.ts new file mode 100644 index 0000000000000..dcde8f52464f9 --- /dev/null +++ b/tests/cases/compiler/spreadsAndContextualTupleTypes.ts @@ -0,0 +1,43 @@ +// @strict: true +// @noEmit: true + +declare function fx1(x: T): T; +declare function fx2(x: T): T; + +const t3 = ['x', 'y', 'z'] as const; + +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 + +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']);