diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c3119b1e64a23..f8b466c3b1afb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18378,6 +18378,32 @@ namespace ts { } // Infer from the members of source and target only if the two types are possibly related if (!typesDefinitelyUnrelated(source, target)) { + if (isArrayType(source) || isTupleType(source)) { + if (isTupleType(target)) { + const sourceLength = isTupleType(source) ? getLengthOfTupleType(source) : 0; + const targetLength = getLengthOfTupleType(target); + const sourceRestType = isTupleType(source) ? getRestTypeOfTupleType(source) : getElementTypeOfArrayType(source); + const targetRestType = getRestTypeOfTupleType(target); + const fixedLength = targetLength < sourceLength || sourceRestType ? targetLength : sourceLength; + for (let i = 0; i < fixedLength; i++) { + inferFromTypes(i < sourceLength ? getTypeArguments(source)[i] : sourceRestType!, getTypeArguments(target)[i]); + } + if (targetRestType) { + const types = fixedLength < sourceLength ? getTypeArguments(source).slice(fixedLength, sourceLength) : []; + if (sourceRestType) { + types.push(sourceRestType); + } + if (types.length) { + inferFromTypes(getUnionType(types), targetRestType); + } + } + return; + } + if (isArrayType(target)) { + inferFromIndexTypes(source, target); + return; + } + } inferFromProperties(source, target); inferFromSignatures(source, target, SignatureKind.Call); inferFromSignatures(source, target, SignatureKind.Construct); @@ -18386,32 +18412,6 @@ namespace ts { } function inferFromProperties(source: Type, target: Type) { - if (isArrayType(source) || isTupleType(source)) { - if (isTupleType(target)) { - const sourceLength = isTupleType(source) ? getLengthOfTupleType(source) : 0; - const targetLength = getLengthOfTupleType(target); - const sourceRestType = isTupleType(source) ? getRestTypeOfTupleType(source) : getElementTypeOfArrayType(source); - const targetRestType = getRestTypeOfTupleType(target); - const fixedLength = targetLength < sourceLength || sourceRestType ? targetLength : sourceLength; - for (let i = 0; i < fixedLength; i++) { - inferFromTypes(i < sourceLength ? getTypeArguments(source)[i] : sourceRestType!, getTypeArguments(target)[i]); - } - if (targetRestType) { - const types = fixedLength < sourceLength ? getTypeArguments(source).slice(fixedLength, sourceLength) : []; - if (sourceRestType) { - types.push(sourceRestType); - } - if (types.length) { - inferFromTypes(getUnionType(types), targetRestType); - } - } - return; - } - if (isArrayType(target)) { - inferFromIndexTypes(source, target); - return; - } - } const properties = getPropertiesOfObjectType(target); for (const targetProp of properties) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); diff --git a/tests/baselines/reference/correctOrderOfPromiseMethod.js b/tests/baselines/reference/correctOrderOfPromiseMethod.js index fadda95374f3e..81a91ba06fd81 100644 --- a/tests/baselines/reference/correctOrderOfPromiseMethod.js +++ b/tests/baselines/reference/correctOrderOfPromiseMethod.js @@ -15,7 +15,7 @@ async function countEverything(): Promise { const [resultA, resultB] = await Promise.all([ providerA(), providerB(), - ] as const); + ]); const dataA: A[] = resultA; const dataB: B[] = resultB; @@ -24,6 +24,10 @@ async function countEverything(): Promise { } return 0; } + +// #31179 + +const expected: Promise<["a", "b", "c"]> = Promise.all(undefined as readonly ["a", "b", "c"]); //// [correctOrderOfPromiseMethod.js] @@ -92,3 +96,5 @@ function countEverything() { }); }); } +// #31179 +var expected = Promise.all(undefined); diff --git a/tests/baselines/reference/correctOrderOfPromiseMethod.symbols b/tests/baselines/reference/correctOrderOfPromiseMethod.symbols index 1e67e9444c67f..29a494f4ec5c3 100644 --- a/tests/baselines/reference/correctOrderOfPromiseMethod.symbols +++ b/tests/baselines/reference/correctOrderOfPromiseMethod.symbols @@ -43,7 +43,7 @@ async function countEverything(): Promise { providerB(), >providerB : Symbol(providerB, Decl(correctOrderOfPromiseMethod.ts, 11, 9)) - ] as const); + ]); const dataA: A[] = resultA; >dataA : Symbol(dataA, Decl(correctOrderOfPromiseMethod.ts, 18, 9)) @@ -70,3 +70,13 @@ async function countEverything(): Promise { return 0; } +// #31179 + +const expected: Promise<["a", "b", "c"]> = Promise.all(undefined as readonly ["a", "b", "c"]); +>expected : Symbol(expected, Decl(correctOrderOfPromiseMethod.ts, 28, 5)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>Promise.all : Symbol(PromiseConstructor.all, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --) ... and 6 more) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>all : Symbol(PromiseConstructor.all, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --) ... and 6 more) +>undefined : Symbol(undefined) + diff --git a/tests/baselines/reference/correctOrderOfPromiseMethod.types b/tests/baselines/reference/correctOrderOfPromiseMethod.types index a6f2c46782e0a..4d7667cc4d847 100644 --- a/tests/baselines/reference/correctOrderOfPromiseMethod.types +++ b/tests/baselines/reference/correctOrderOfPromiseMethod.types @@ -28,13 +28,12 @@ async function countEverything(): Promise { const [resultA, resultB] = await Promise.all([ >resultA : A[] >resultB : B[] ->await Promise.all([ providerA(), providerB(), ] as const) : [A[], B[]] ->Promise.all([ providerA(), providerB(), ] as const) : Promise<[A[], B[]]> +>await Promise.all([ providerA(), providerB(), ]) : [A[], B[]] +>Promise.all([ providerA(), providerB(), ]) : Promise<[A[], B[]]> >Promise.all : { (values: Iterable>): Promise; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike, T10 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike]): Promise<[T1, T2, T3, T4, T5]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike]): Promise<[T1, T2, T3, T4]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike]): Promise<[T1, T2, T3]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; (values: readonly (T | PromiseLike)[]): Promise; } >Promise : PromiseConstructor >all : { (values: Iterable>): Promise; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike, T10 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike]): Promise<[T1, T2, T3, T4, T5]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike]): Promise<[T1, T2, T3, T4]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike]): Promise<[T1, T2, T3]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; (values: readonly (T | PromiseLike)[]): Promise; } ->[ providerA(), providerB(), ] as const : readonly [Promise, Promise] ->[ providerA(), providerB(), ] : readonly [Promise, Promise] +>[ providerA(), providerB(), ] : [Promise, Promise] providerA(), >providerA() : Promise @@ -44,7 +43,7 @@ async function countEverything(): Promise { >providerB() : Promise >providerB : () => Promise - ] as const); + ]); const dataA: A[] = resultA; >dataA : A[] @@ -72,3 +71,14 @@ async function countEverything(): Promise { >0 : 0 } +// #31179 + +const expected: Promise<["a", "b", "c"]> = Promise.all(undefined as readonly ["a", "b", "c"]); +>expected : Promise<["a", "b", "c"]> +>Promise.all(undefined as readonly ["a", "b", "c"]) : Promise<["a", "b", "c"]> +>Promise.all : { (values: Iterable>): Promise; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike, T10 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike]): Promise<[T1, T2, T3, T4, T5]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike]): Promise<[T1, T2, T3, T4]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike]): Promise<[T1, T2, T3]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; (values: readonly (T | PromiseLike)[]): Promise; } +>Promise : PromiseConstructor +>all : { (values: Iterable>): Promise; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike, T10 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike, T6 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike, T5 | PromiseLike]): Promise<[T1, T2, T3, T4, T5]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike]): Promise<[T1, T2, T3, T4]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike]): Promise<[T1, T2, T3]>; (values: readonly [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; (values: readonly (T | PromiseLike)[]): Promise; } +>undefined as readonly ["a", "b", "c"] : readonly ["a", "b", "c"] +>undefined : undefined + diff --git a/tests/baselines/reference/restTupleElements1.types b/tests/baselines/reference/restTupleElements1.types index bc2bc2511a608..737d14e2e91f4 100644 --- a/tests/baselines/reference/restTupleElements1.types +++ b/tests/baselines/reference/restTupleElements1.types @@ -173,7 +173,7 @@ f0([]); // Error >[] : [] f0([1]); ->f0([1]) : [number, number] +>f0([1]) : [number, unknown] >f0 : (x: [T, ...U[]]) => [T, U] >[1] : [number] >1 : 1 diff --git a/tests/baselines/reference/typeInferenceWithTupleType.js b/tests/baselines/reference/typeInferenceWithTupleType.js index 10a18274c73dd..a883b03b0fa39 100644 --- a/tests/baselines/reference/typeInferenceWithTupleType.js +++ b/tests/baselines/reference/typeInferenceWithTupleType.js @@ -23,6 +23,14 @@ var zipResult = zip(["foo", "bar"], [5, 6]); var zipResultEle = zipResult[0]; // [string, number] var zipResultEleEle = zipResult[0][0]; // string +// #33559 and #33752 + +declare function f1(values: [T1[], T2[]]): T1; +declare function f2(values: readonly [T1[], T2[]]): T1; + +let expected: "a"; +expected = f1(undefined as ["a"[], "b"[]]); +expected = f2(undefined as ["a"[], "b"[]]); //// [typeInferenceWithTupleType.js] @@ -46,3 +54,6 @@ function zip(array1, array2) { var zipResult = zip(["foo", "bar"], [5, 6]); var zipResultEle = zipResult[0]; // [string, number] var zipResultEleEle = zipResult[0][0]; // string +var expected; +expected = f1(undefined); +expected = f2(undefined); diff --git a/tests/baselines/reference/typeInferenceWithTupleType.symbols b/tests/baselines/reference/typeInferenceWithTupleType.symbols index 040e7bed1b10b..a7d71d553b42d 100644 --- a/tests/baselines/reference/typeInferenceWithTupleType.symbols +++ b/tests/baselines/reference/typeInferenceWithTupleType.symbols @@ -97,4 +97,36 @@ var zipResultEleEle = zipResult[0][0]; // string >0 : Symbol(0) >0 : Symbol(0) +// #33559 and #33752 + +declare function f1(values: [T1[], T2[]]): T1; +>f1 : Symbol(f1, Decl(typeInferenceWithTupleType.ts, 22, 38)) +>T1 : Symbol(T1, Decl(typeInferenceWithTupleType.ts, 26, 20)) +>T2 : Symbol(T2, Decl(typeInferenceWithTupleType.ts, 26, 23)) +>values : Symbol(values, Decl(typeInferenceWithTupleType.ts, 26, 28)) +>T1 : Symbol(T1, Decl(typeInferenceWithTupleType.ts, 26, 20)) +>T2 : Symbol(T2, Decl(typeInferenceWithTupleType.ts, 26, 23)) +>T1 : Symbol(T1, Decl(typeInferenceWithTupleType.ts, 26, 20)) + +declare function f2(values: readonly [T1[], T2[]]): T1; +>f2 : Symbol(f2, Decl(typeInferenceWithTupleType.ts, 26, 54)) +>T1 : Symbol(T1, Decl(typeInferenceWithTupleType.ts, 27, 20)) +>T2 : Symbol(T2, Decl(typeInferenceWithTupleType.ts, 27, 23)) +>values : Symbol(values, Decl(typeInferenceWithTupleType.ts, 27, 28)) +>T1 : Symbol(T1, Decl(typeInferenceWithTupleType.ts, 27, 20)) +>T2 : Symbol(T2, Decl(typeInferenceWithTupleType.ts, 27, 23)) +>T1 : Symbol(T1, Decl(typeInferenceWithTupleType.ts, 27, 20)) + +let expected: "a"; +>expected : Symbol(expected, Decl(typeInferenceWithTupleType.ts, 29, 3)) + +expected = f1(undefined as ["a"[], "b"[]]); +>expected : Symbol(expected, Decl(typeInferenceWithTupleType.ts, 29, 3)) +>f1 : Symbol(f1, Decl(typeInferenceWithTupleType.ts, 22, 38)) +>undefined : Symbol(undefined) + +expected = f2(undefined as ["a"[], "b"[]]); +>expected : Symbol(expected, Decl(typeInferenceWithTupleType.ts, 29, 3)) +>f2 : Symbol(f2, Decl(typeInferenceWithTupleType.ts, 26, 54)) +>undefined : Symbol(undefined) diff --git a/tests/baselines/reference/typeInferenceWithTupleType.types b/tests/baselines/reference/typeInferenceWithTupleType.types index 4e407964bb0df..6eb131538e1bb 100644 --- a/tests/baselines/reference/typeInferenceWithTupleType.types +++ b/tests/baselines/reference/typeInferenceWithTupleType.types @@ -109,4 +109,32 @@ var zipResultEleEle = zipResult[0][0]; // string >0 : 0 >0 : 0 +// #33559 and #33752 + +declare function f1(values: [T1[], T2[]]): T1; +>f1 : (values: [T1[], T2[]]) => T1 +>values : [T1[], T2[]] + +declare function f2(values: readonly [T1[], T2[]]): T1; +>f2 : (values: readonly [T1[], T2[]]) => T1 +>values : readonly [T1[], T2[]] + +let expected: "a"; +>expected : "a" + +expected = f1(undefined as ["a"[], "b"[]]); +>expected = f1(undefined as ["a"[], "b"[]]) : "a" +>expected : "a" +>f1(undefined as ["a"[], "b"[]]) : "a" +>f1 : (values: [T1[], T2[]]) => T1 +>undefined as ["a"[], "b"[]] : ["a"[], "b"[]] +>undefined : undefined + +expected = f2(undefined as ["a"[], "b"[]]); +>expected = f2(undefined as ["a"[], "b"[]]) : "a" +>expected : "a" +>f2(undefined as ["a"[], "b"[]]) : "a" +>f2 : (values: readonly [T1[], T2[]]) => T1 +>undefined as ["a"[], "b"[]] : ["a"[], "b"[]] +>undefined : undefined diff --git a/tests/cases/compiler/correctOrderOfPromiseMethod.ts b/tests/cases/compiler/correctOrderOfPromiseMethod.ts index 70c730c6b20b1..7ece852204825 100644 --- a/tests/cases/compiler/correctOrderOfPromiseMethod.ts +++ b/tests/cases/compiler/correctOrderOfPromiseMethod.ts @@ -17,7 +17,7 @@ async function countEverything(): Promise { const [resultA, resultB] = await Promise.all([ providerA(), providerB(), - ] as const); + ]); const dataA: A[] = resultA; const dataB: B[] = resultB; @@ -26,3 +26,7 @@ async function countEverything(): Promise { } return 0; } + +// #31179 + +const expected: Promise<["a", "b", "c"]> = Promise.all(undefined as readonly ["a", "b", "c"]); diff --git a/tests/cases/conformance/types/tuple/typeInferenceWithTupleType.ts b/tests/cases/conformance/types/tuple/typeInferenceWithTupleType.ts index d19353d29ef02..e1ba48b0ddf44 100644 --- a/tests/cases/conformance/types/tuple/typeInferenceWithTupleType.ts +++ b/tests/cases/conformance/types/tuple/typeInferenceWithTupleType.ts @@ -1,4 +1,4 @@ -function combine(x: T, y: U): [T, U] { +function combine(x: T, y: U): [T, U] { return [x, y]; } @@ -22,3 +22,11 @@ var zipResult = zip(["foo", "bar"], [5, 6]); var zipResultEle = zipResult[0]; // [string, number] var zipResultEleEle = zipResult[0][0]; // string +// #33559 and #33752 + +declare function f1(values: [T1[], T2[]]): T1; +declare function f2(values: readonly [T1[], T2[]]): T1; + +let expected: "a"; +expected = f1(undefined as ["a"[], "b"[]]); +expected = f2(undefined as ["a"[], "b"[]]);