From f424dfc18a01c45b8709950b6e2dc3beafaf9960 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 26 Oct 2021 11:54:46 -0700 Subject: [PATCH] Slightly less conservative check in isConstraintPosition (#46526) * Slight adjustment to check in isConstraintPosition * Add regression test --- src/compiler/checker.ts | 2 +- .../controlFlowGenericTypes.errors.txt | 20 +++++++ .../reference/controlFlowGenericTypes.js | 25 +++++++++ .../reference/controlFlowGenericTypes.symbols | 52 +++++++++++++++++++ .../reference/controlFlowGenericTypes.types | 41 +++++++++++++++ .../controlFlow/controlFlowGenericTypes.ts | 20 +++++++ 6 files changed, 159 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c647af631..a33f986723 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24758,7 +24758,7 @@ namespace ts { return parent.kind === SyntaxKind.PropertyAccessExpression || parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === node || parent.kind === SyntaxKind.ElementAccessExpression && (parent as ElementAccessExpression).expression === node && - !(isGenericTypeWithoutNullableConstraint(type) && isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression))); + !(someType(type, isGenericTypeWithoutNullableConstraint) && isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression))); } function isGenericTypeWithUnionConstraint(type: Type) { diff --git a/tests/baselines/reference/controlFlowGenericTypes.errors.txt b/tests/baselines/reference/controlFlowGenericTypes.errors.txt index 620c3ebb6d..3130ba416f 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.errors.txt +++ b/tests/baselines/reference/controlFlowGenericTypes.errors.txt @@ -220,4 +220,24 @@ tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(168,9): error TS2 this.validateRow(row); } } + + // Repro from #46495 + + interface Button { + type: "button"; + text: string; + } + + interface Checkbox { + type: "checkbox"; + isChecked: boolean; + } + + type Control = Button | Checkbox; + + function update(control : T | undefined, key: K, value: T[K]): void { + if (control !== undefined) { + control[key] = value; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowGenericTypes.js b/tests/baselines/reference/controlFlowGenericTypes.js index d080434b17..a59b496ecd 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.js +++ b/tests/baselines/reference/controlFlowGenericTypes.js @@ -190,6 +190,26 @@ class SqlTable { this.validateRow(row); } } + +// Repro from #46495 + +interface Button { + type: "button"; + text: string; +} + +interface Checkbox { + type: "checkbox"; + isChecked: boolean; +} + +type Control = Button | Checkbox; + +function update(control : T | undefined, key: K, value: T[K]): void { + if (control !== undefined) { + control[key] = value; + } +} //// [controlFlowGenericTypes.js] @@ -343,3 +363,8 @@ var SqlTable = /** @class */ (function () { }; return SqlTable; }()); +function update(control, key, value) { + if (control !== undefined) { + control[key] = value; + } +} diff --git a/tests/baselines/reference/controlFlowGenericTypes.symbols b/tests/baselines/reference/controlFlowGenericTypes.symbols index c940c7983d..1a1edf72f0 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.symbols +++ b/tests/baselines/reference/controlFlowGenericTypes.symbols @@ -574,3 +574,55 @@ class SqlTable { } } +// Repro from #46495 + +interface Button { +>Button : Symbol(Button, Decl(controlFlowGenericTypes.ts, 190, 1)) + + type: "button"; +>type : Symbol(Button.type, Decl(controlFlowGenericTypes.ts, 194, 18)) + + text: string; +>text : Symbol(Button.text, Decl(controlFlowGenericTypes.ts, 195, 19)) +} + +interface Checkbox { +>Checkbox : Symbol(Checkbox, Decl(controlFlowGenericTypes.ts, 197, 1)) + + type: "checkbox"; +>type : Symbol(Checkbox.type, Decl(controlFlowGenericTypes.ts, 199, 20)) + + isChecked: boolean; +>isChecked : Symbol(Checkbox.isChecked, Decl(controlFlowGenericTypes.ts, 200, 21)) +} + +type Control = Button | Checkbox; +>Control : Symbol(Control, Decl(controlFlowGenericTypes.ts, 202, 1)) +>Button : Symbol(Button, Decl(controlFlowGenericTypes.ts, 190, 1)) +>Checkbox : Symbol(Checkbox, Decl(controlFlowGenericTypes.ts, 197, 1)) + +function update(control : T | undefined, key: K, value: T[K]): void { +>update : Symbol(update, Decl(controlFlowGenericTypes.ts, 204, 33)) +>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 206, 16)) +>Control : Symbol(Control, Decl(controlFlowGenericTypes.ts, 202, 1)) +>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 206, 34)) +>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 206, 16)) +>control : Symbol(control, Decl(controlFlowGenericTypes.ts, 206, 54)) +>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 206, 16)) +>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 206, 78)) +>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 206, 34)) +>value : Symbol(value, Decl(controlFlowGenericTypes.ts, 206, 86)) +>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 206, 16)) +>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 206, 34)) + + if (control !== undefined) { +>control : Symbol(control, Decl(controlFlowGenericTypes.ts, 206, 54)) +>undefined : Symbol(undefined) + + control[key] = value; +>control : Symbol(control, Decl(controlFlowGenericTypes.ts, 206, 54)) +>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 206, 78)) +>value : Symbol(value, Decl(controlFlowGenericTypes.ts, 206, 86)) + } +} + diff --git a/tests/baselines/reference/controlFlowGenericTypes.types b/tests/baselines/reference/controlFlowGenericTypes.types index 5bf18f4e37..6ee07abf4d 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.types +++ b/tests/baselines/reference/controlFlowGenericTypes.types @@ -542,3 +542,44 @@ class SqlTable { } } +// Repro from #46495 + +interface Button { + type: "button"; +>type : "button" + + text: string; +>text : string +} + +interface Checkbox { + type: "checkbox"; +>type : "checkbox" + + isChecked: boolean; +>isChecked : boolean +} + +type Control = Button | Checkbox; +>Control : Control + +function update(control : T | undefined, key: K, value: T[K]): void { +>update : (control: T | undefined, key: K, value: T[K]) => void +>control : T | undefined +>key : K +>value : T[K] + + if (control !== undefined) { +>control !== undefined : boolean +>control : T | undefined +>undefined : undefined + + control[key] = value; +>control[key] = value : T[K] +>control[key] : T[K] +>control : T +>key : K +>value : T[K] + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts b/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts index f2cac082f5..3e67f3247d 100644 --- a/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts +++ b/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts @@ -191,3 +191,23 @@ class SqlTable { this.validateRow(row); } } + +// Repro from #46495 + +interface Button { + type: "button"; + text: string; +} + +interface Checkbox { + type: "checkbox"; + isChecked: boolean; +} + +type Control = Button | Checkbox; + +function update(control : T | undefined, key: K, value: T[K]): void { + if (control !== undefined) { + control[key] = value; + } +}