diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 967ad86941b40..f8258d1cafcf9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23272,7 +23272,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isTupleLikeType(type: Type): boolean { - return isTupleType(type) || !!getPropertyOfType(type, "0" as __String); + let lengthType; + return isTupleType(type) || + !!getPropertyOfType(type, "0" as __String) || + isArrayLikeType(type) && !!(lengthType = getTypeOfPropertyOfType(type, "length" as __String)) && everyType(lengthType, t => !!(t.flags & TypeFlags.NumberLiteral)); } function isArrayOrTupleLikeType(type: Type): boolean { diff --git a/tests/baselines/reference/contextualTypeWithTuple.errors.txt b/tests/baselines/reference/contextualTypeWithTuple.errors.txt index 3690954993291..44c9ac778efe6 100644 --- a/tests/baselines/reference/contextualTypeWithTuple.errors.txt +++ b/tests/baselines/reference/contextualTypeWithTuple.errors.txt @@ -68,4 +68,15 @@ contextualTypeWithTuple.ts(25,1): error TS2322: Type '[number, string | number]' !!! error TS2322: Type '[number, string | number]' is not assignable to type '[number, string]'. !!! error TS2322: Type at position 1 in source is not compatible with type at position 1 in target. !!! error TS2322: Type 'string | number' is not assignable to type 'string'. -!!! error TS2322: Type 'number' is not assignable to type 'string'. \ No newline at end of file +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + // repro from #29311 + type test1 = [...number[]] + type fixed1 = test1 & { length: 2 } + let var1: fixed1 = [0, 0] + + // #52551 + type EmptyTuple = [] + interface MyEmptyTuple extends EmptyTuple { extraInfo?: any; } + const withExtra: MyEmptyTuple = [] + \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypeWithTuple.js b/tests/baselines/reference/contextualTypeWithTuple.js index b81e9650d1a2c..589ebd29bf9db 100644 --- a/tests/baselines/reference/contextualTypeWithTuple.js +++ b/tests/baselines/reference/contextualTypeWithTuple.js @@ -25,7 +25,18 @@ var strStrTuple: [string, string] = ["foo", "bar", 5]; unionTuple = unionTuple1; unionTuple = unionTuple2; unionTuple2 = unionTuple; -numStrTuple = unionTuple3; +numStrTuple = unionTuple3; + +// repro from #29311 +type test1 = [...number[]] +type fixed1 = test1 & { length: 2 } +let var1: fixed1 = [0, 0] + +// #52551 +type EmptyTuple = [] +interface MyEmptyTuple extends EmptyTuple { extraInfo?: any; } +const withExtra: MyEmptyTuple = [] + //// [contextualTypeWithTuple.js] // no error @@ -58,3 +69,5 @@ unionTuple = unionTuple1; unionTuple = unionTuple2; unionTuple2 = unionTuple; numStrTuple = unionTuple3; +var var1 = [0, 0]; +var withExtra = []; diff --git a/tests/baselines/reference/contextualTypeWithTuple.symbols b/tests/baselines/reference/contextualTypeWithTuple.symbols index 56b33b0e69911..3af9d7e73a2a4 100644 --- a/tests/baselines/reference/contextualTypeWithTuple.symbols +++ b/tests/baselines/reference/contextualTypeWithTuple.symbols @@ -82,3 +82,29 @@ numStrTuple = unionTuple3; >numStrTuple : Symbol(numStrTuple, Decl(contextualTypeWithTuple.ts, 1, 3)) >unionTuple3 : Symbol(unionTuple3, Decl(contextualTypeWithTuple.ts, 11, 3)) +// repro from #29311 +type test1 = [...number[]] +>test1 : Symbol(test1, Decl(contextualTypeWithTuple.ts, 24, 26)) + +type fixed1 = test1 & { length: 2 } +>fixed1 : Symbol(fixed1, Decl(contextualTypeWithTuple.ts, 27, 26)) +>test1 : Symbol(test1, Decl(contextualTypeWithTuple.ts, 24, 26)) +>length : Symbol(length, Decl(contextualTypeWithTuple.ts, 28, 23)) + +let var1: fixed1 = [0, 0] +>var1 : Symbol(var1, Decl(contextualTypeWithTuple.ts, 29, 3)) +>fixed1 : Symbol(fixed1, Decl(contextualTypeWithTuple.ts, 27, 26)) + +// #52551 +type EmptyTuple = [] +>EmptyTuple : Symbol(EmptyTuple, Decl(contextualTypeWithTuple.ts, 29, 25)) + +interface MyEmptyTuple extends EmptyTuple { extraInfo?: any; } +>MyEmptyTuple : Symbol(MyEmptyTuple, Decl(contextualTypeWithTuple.ts, 32, 20)) +>EmptyTuple : Symbol(EmptyTuple, Decl(contextualTypeWithTuple.ts, 29, 25)) +>extraInfo : Symbol(MyEmptyTuple.extraInfo, Decl(contextualTypeWithTuple.ts, 33, 43)) + +const withExtra: MyEmptyTuple = [] +>withExtra : Symbol(withExtra, Decl(contextualTypeWithTuple.ts, 34, 5)) +>MyEmptyTuple : Symbol(MyEmptyTuple, Decl(contextualTypeWithTuple.ts, 32, 20)) + diff --git a/tests/baselines/reference/contextualTypeWithTuple.types b/tests/baselines/reference/contextualTypeWithTuple.types index 7b935aa828ed7..75b13fa949107 100644 --- a/tests/baselines/reference/contextualTypeWithTuple.types +++ b/tests/baselines/reference/contextualTypeWithTuple.types @@ -128,3 +128,28 @@ numStrTuple = unionTuple3; >numStrTuple : [number, string] >unionTuple3 : [number, string | number] +// repro from #29311 +type test1 = [...number[]] +>test1 : number[] + +type fixed1 = test1 & { length: 2 } +>fixed1 : test1 & { length: 2; } +>length : 2 + +let var1: fixed1 = [0, 0] +>var1 : fixed1 +>[0, 0] : [number, number] +>0 : 0 +>0 : 0 + +// #52551 +type EmptyTuple = [] +>EmptyTuple : [] + +interface MyEmptyTuple extends EmptyTuple { extraInfo?: any; } +>extraInfo : any + +const withExtra: MyEmptyTuple = [] +>withExtra : MyEmptyTuple +>[] : [] + diff --git a/tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts b/tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts index 9600ebcd6713d..a02e5c839799c 100644 --- a/tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts +++ b/tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts @@ -22,4 +22,14 @@ var strStrTuple: [string, string] = ["foo", "bar", 5]; unionTuple = unionTuple1; unionTuple = unionTuple2; unionTuple2 = unionTuple; -numStrTuple = unionTuple3; \ No newline at end of file +numStrTuple = unionTuple3; + +// repro from #29311 +type test1 = [...number[]] +type fixed1 = test1 & { length: 2 } +let var1: fixed1 = [0, 0] + +// #52551 +type EmptyTuple = [] +interface MyEmptyTuple extends EmptyTuple { extraInfo?: any; } +const withExtra: MyEmptyTuple = []