From 01b0ba85ebaaed1fa1364d8b53c6a119c9ed070b Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Sat, 5 Nov 2022 19:39:27 -0700 Subject: [PATCH 1/6] Add failing test --- tests/cases/fourslash/completionsTupleUnion.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/cases/fourslash/completionsTupleUnion.ts diff --git a/tests/cases/fourslash/completionsTupleUnion.ts b/tests/cases/fourslash/completionsTupleUnion.ts new file mode 100644 index 0000000000000..e71c65616b27f --- /dev/null +++ b/tests/cases/fourslash/completionsTupleUnion.ts @@ -0,0 +1,5 @@ +/// + +////declare const x: ['a', 'b'] | ['c', 'd'] = ["a", /**/]; + +verify.completions({ marker: "", exact: "b", isNewIdentifierLocation: true }); From 064c65af7ce4cddd02b9221931cdf855d06c9237 Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Sun, 6 Nov 2022 01:26:56 -0800 Subject: [PATCH 2/6] Another failing test --- src/compiler/checker.ts | 14 +++++++++++++- tests/cases/fourslash/completionsTupleUnion.ts | 5 ----- tests/cases/fourslash/completionsTupleUnion1.ts | 6 ++++++ tests/cases/fourslash/completionsTupleUnion2.ts | 5 +++++ tests/cases/fourslash/completionsTupleUnion3.ts | 5 +++++ 5 files changed, 29 insertions(+), 6 deletions(-) delete mode 100644 tests/cases/fourslash/completionsTupleUnion.ts create mode 100644 tests/cases/fourslash/completionsTupleUnion1.ts create mode 100644 tests/cases/fourslash/completionsTupleUnion2.ts create mode 100644 tests/cases/fourslash/completionsTupleUnion3.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1bacbfbead2e4..e1ecfdfccdf47 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27667,7 +27667,19 @@ namespace ts { case SyntaxKind.ArrayLiteralExpression: { const arrayLiteral = parent as ArrayLiteralExpression; const type = getApparentTypeOfContextualType(arrayLiteral, contextFlags); - return getContextualTypeForElementExpression(type, indexOfNode(arrayLiteral.elements, node)); + if (!type) return undefined; + const nodeIndex = indexOfNode(arrayLiteral.elements, node); + let filteredType = type; + for(let i = 0; i < nodeIndex; i++) { + const expectedType = getTypeOfExpression(arrayLiteral.elements[i]); + if (getObjectFlags(expectedType) & ObjectFlags.ContainsObjectOrArrayLiteral) continue; + filteredType = filterType(filteredType, (t) => { + const indexType = getTypeOfPropertyOfContextualType(t, "" + i as __String); + if (!indexType) return false; + return checkTypeRelatedTo(expectedType, indexType, comparableRelation, /** errorNode */ undefined); + }); + } + return getContextualTypeForElementExpression(filteredType, nodeIndex); } case SyntaxKind.ConditionalExpression: return getContextualTypeForConditionalOperand(node, contextFlags); diff --git a/tests/cases/fourslash/completionsTupleUnion.ts b/tests/cases/fourslash/completionsTupleUnion.ts deleted file mode 100644 index e71c65616b27f..0000000000000 --- a/tests/cases/fourslash/completionsTupleUnion.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// - -////declare const x: ['a', 'b'] | ['c', 'd'] = ["a", /**/]; - -verify.completions({ marker: "", exact: "b", isNewIdentifierLocation: true }); diff --git a/tests/cases/fourslash/completionsTupleUnion1.ts b/tests/cases/fourslash/completionsTupleUnion1.ts new file mode 100644 index 0000000000000..4f625f08cdad6 --- /dev/null +++ b/tests/cases/fourslash/completionsTupleUnion1.ts @@ -0,0 +1,6 @@ +/// + +////declare const a: "a"; +////declare const x: ['a', 'b'] | ['a', 'c'] | ['d', 'e'] = [a, "/**/"]; + +verify.completions({ marker: "", exact: ["b", "c"] }); diff --git a/tests/cases/fourslash/completionsTupleUnion2.ts b/tests/cases/fourslash/completionsTupleUnion2.ts new file mode 100644 index 0000000000000..c584e435557bf --- /dev/null +++ b/tests/cases/fourslash/completionsTupleUnion2.ts @@ -0,0 +1,5 @@ +/// + +////declare const x: ["a", ["b"], "x", ["y"]] = ["a", ["/**/"]] + +verify.completions({ marker: "", exact: ["b"] }); diff --git a/tests/cases/fourslash/completionsTupleUnion3.ts b/tests/cases/fourslash/completionsTupleUnion3.ts new file mode 100644 index 0000000000000..2d4df8a41ba93 --- /dev/null +++ b/tests/cases/fourslash/completionsTupleUnion3.ts @@ -0,0 +1,5 @@ +/// + +////declare const x: [{ t: "a" }, "b"] | [{ t: "b" }, "c"] = [{ t: a }, "/**/"] + +verify.completions({ marker: "", exact: ["b"] }); From 8bc78fd5cc45f2bc8c8b84ebcec88eed5463ff6a Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Sun, 6 Nov 2022 12:03:11 -0800 Subject: [PATCH 3/6] Only do it for unions --- src/compiler/checker.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e1ecfdfccdf47..577ef35a0b844 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27670,14 +27670,16 @@ namespace ts { if (!type) return undefined; const nodeIndex = indexOfNode(arrayLiteral.elements, node); let filteredType = type; - for(let i = 0; i < nodeIndex; i++) { - const expectedType = getTypeOfExpression(arrayLiteral.elements[i]); - if (getObjectFlags(expectedType) & ObjectFlags.ContainsObjectOrArrayLiteral) continue; - filteredType = filterType(filteredType, (t) => { - const indexType = getTypeOfPropertyOfContextualType(t, "" + i as __String); - if (!indexType) return false; - return checkTypeRelatedTo(expectedType, indexType, comparableRelation, /** errorNode */ undefined); - }); + if (type.flags & TypeFlags.Union) { + for(let i = 0; i < nodeIndex; i++) { + const expType = getTypeOfExpression(arrayLiteral.elements[i]); + if (getObjectFlags(expType) & ObjectFlags.ArrayLiteral) continue; + filteredType = filterType(filteredType, (t) => { + const branchType = getTypeOfPropertyOfContextualType(t, "" + i as __String); + if (!branchType) return false; + return isTypeComparableTo(branchType, expType); + }); + } } return getContextualTypeForElementExpression(filteredType, nodeIndex); } From da2c233eecdf3f65a942e76573ce34ec49fc9d86 Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Sun, 6 Nov 2022 12:03:16 -0800 Subject: [PATCH 4/6] tests --- .../fourslash/{completionsTuple.ts => completionsTuple1.ts} | 0 .../{completionsTupleUnion3.ts => completionsTuple2.ts} | 2 +- tests/cases/fourslash/completionsTuple3.ts | 5 +++++ tests/cases/fourslash/completionsTupleUnion2.ts | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) rename tests/cases/fourslash/{completionsTuple.ts => completionsTuple1.ts} (100%) rename tests/cases/fourslash/{completionsTupleUnion3.ts => completionsTuple2.ts} (52%) create mode 100644 tests/cases/fourslash/completionsTuple3.ts diff --git a/tests/cases/fourslash/completionsTuple.ts b/tests/cases/fourslash/completionsTuple1.ts similarity index 100% rename from tests/cases/fourslash/completionsTuple.ts rename to tests/cases/fourslash/completionsTuple1.ts diff --git a/tests/cases/fourslash/completionsTupleUnion3.ts b/tests/cases/fourslash/completionsTuple2.ts similarity index 52% rename from tests/cases/fourslash/completionsTupleUnion3.ts rename to tests/cases/fourslash/completionsTuple2.ts index 2d4df8a41ba93..0e35c81c05ddb 100644 --- a/tests/cases/fourslash/completionsTupleUnion3.ts +++ b/tests/cases/fourslash/completionsTuple2.ts @@ -1,5 +1,5 @@ /// -////declare const x: [{ t: "a" }, "b"] | [{ t: "b" }, "c"] = [{ t: a }, "/**/"] +////declare const x: ["a", "b"] = ["x", "/**/"] verify.completions({ marker: "", exact: ["b"] }); diff --git a/tests/cases/fourslash/completionsTuple3.ts b/tests/cases/fourslash/completionsTuple3.ts new file mode 100644 index 0000000000000..c584e435557bf --- /dev/null +++ b/tests/cases/fourslash/completionsTuple3.ts @@ -0,0 +1,5 @@ +/// + +////declare const x: ["a", ["b"], "x", ["y"]] = ["a", ["/**/"]] + +verify.completions({ marker: "", exact: ["b"] }); diff --git a/tests/cases/fourslash/completionsTupleUnion2.ts b/tests/cases/fourslash/completionsTupleUnion2.ts index c584e435557bf..4c6dd0eca3139 100644 --- a/tests/cases/fourslash/completionsTupleUnion2.ts +++ b/tests/cases/fourslash/completionsTupleUnion2.ts @@ -1,5 +1,6 @@ /// -////declare const x: ["a", ["b"], "x", ["y"]] = ["a", ["/**/"]] +////declare const a: "a"; +////declare const x: [{ t: "a" }, "b"] | [{ t: "b" }, "c"] = [{ t: a }, "/**/"] verify.completions({ marker: "", exact: ["b"] }); From 80e7a5080c604fe37b5fefc0398426687d6610c5 Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Sun, 6 Nov 2022 12:09:57 -0800 Subject: [PATCH 5/6] Add comment --- src/compiler/checker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 577ef35a0b844..3722e42aeda3b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27671,6 +27671,8 @@ namespace ts { const nodeIndex = indexOfNode(arrayLiteral.elements, node); let filteredType = type; if (type.flags & TypeFlags.Union) { + // If there is a union of tuples, filter down to only valid members of the union + // based on previous elements in the tuple for(let i = 0; i < nodeIndex; i++) { const expType = getTypeOfExpression(arrayLiteral.elements[i]); if (getObjectFlags(expType) & ObjectFlags.ArrayLiteral) continue; From 4095f4a79eb92c43bbbbffcf5b3e4baa8913efb1 Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Sun, 6 Nov 2022 13:15:12 -0800 Subject: [PATCH 6/6] Fix tests --- 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 3722e42aeda3b..2cab4319e2d2a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27671,15 +27671,15 @@ namespace ts { const nodeIndex = indexOfNode(arrayLiteral.elements, node); let filteredType = type; if (type.flags & TypeFlags.Union) { - // If there is a union of tuples, filter down to only valid members of the union + // If there is a union of tuples, filter down to only valid variants of the union // based on previous elements in the tuple for(let i = 0; i < nodeIndex; i++) { - const expType = getTypeOfExpression(arrayLiteral.elements[i]); - if (getObjectFlags(expType) & ObjectFlags.ArrayLiteral) continue; + const observedType = getTypeOfExpression(arrayLiteral.elements[i]); + if (getObjectFlags(observedType) & ObjectFlags.ArrayLiteral) continue; filteredType = filterType(filteredType, (t) => { - const branchType = getTypeOfPropertyOfContextualType(t, "" + i as __String); - if (!branchType) return false; - return isTypeComparableTo(branchType, expType); + const variantType = getTypeOfPropertyOfContextualType(t, "" + i as __String); + if (!variantType) return false; + return areTypesComparable(variantType, observedType); }); } }