From 53f5ca748fad7103966fa0f3b770a1e83d5e34bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 7 Sep 2026 10:05:00 +0200 Subject: [PATCH] Fix narrowing of generic this parameters --- tsc/internal/checker/checker.go | 21 +- .../controlFlowGenericThis.errors.txt | 81 ++++++ .../compiler/controlFlowGenericThis.symbols | 213 +++++++++++++++ .../compiler/controlFlowGenericThis.types | 214 +++++++++++++++ .../controlFlowGenericThisContextual.symbols | 254 ++++++++++++++++++ .../controlFlowGenericThisContextual.types | 238 ++++++++++++++++ .../cases/compiler/controlFlowGenericThis.ts | 71 +++++ .../controlFlowGenericThisContextual.ts | 76 ++++++ 8 files changed, 1158 insertions(+), 10 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.types create mode 100644 tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.types create mode 100644 tsc/testdata/tests/cases/compiler/controlFlowGenericThis.ts create mode 100644 tsc/testdata/tests/cases/compiler/controlFlowGenericThisContextual.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index ca77e97af8547..bed39fac4f23b 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -7905,7 +7905,7 @@ func (c *Checker) checkExpressionWorker(node *ast.Node, checkMode CheckMode) *Ty case ast.KindPrivateIdentifier: return c.checkPrivateIdentifierExpression(node) case ast.KindThisKeyword: - return c.checkThisExpression(node) + return c.checkThisExpression(node, checkMode) case ast.KindSuperKeyword: return c.checkSuperExpression(node) case ast.KindNullKeyword: @@ -8293,7 +8293,7 @@ func (c *Checker) checkQualifiedName(node *ast.Node, checkMode CheckMode) *Type left := node.AsQualifiedName().Left var leftType *Type if ast.IsPartOfTypeQuery(node) && ast.IsThisIdentifier(left) { - leftType = c.checkNonNullType(c.checkThisExpression(left), left) + leftType = c.checkNonNullType(c.checkThisExpression(left, CheckModeNormal), left) } else { leftType = c.checkNonNullExpression(left) } @@ -10823,7 +10823,7 @@ func (c *Checker) checkExpressionWithTypeArguments(node *ast.Node) *Type { } else { exprName := node.AsTypeQueryNode().ExprName if ast.IsThisIdentifier(exprName) { - exprType = c.checkThisExpression(node.AsTypeQueryNode().ExprName) + exprType = c.checkThisExpression(node.AsTypeQueryNode().ExprName, CheckModeNormal) } else { exprType = c.checkExpression(node.AsTypeQueryNode().ExprName) } @@ -11218,7 +11218,7 @@ func (c *Checker) checkSyntheticExpression(node *ast.Node) *Type { func (c *Checker) checkIdentifier(node *ast.Node, checkMode CheckMode) *Type { if ast.IsThisInTypeQuery(node) { - return c.checkThisExpression(node) + return c.checkThisExpression(node, checkMode) } symbol := c.getResolvedSymbol(node) if symbol == c.unknownSymbol { @@ -12253,7 +12253,7 @@ func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type { return nil } -func (c *Checker) checkThisExpression(node *ast.Node) *Type { +func (c *Checker) checkThisExpression(node *ast.Node, checkMode CheckMode) *Type { // Stop at the first arrow function so that we can // tell whether 'this' needs to be captured. container := ast.GetThisContainer(node, true /*includeArrowFunctions*/, true /*includeClassComputedPropertyName*/) @@ -12288,7 +12288,7 @@ func (c *Checker) checkThisExpression(node *ast.Node) *Type { // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks } } - t := c.tryGetThisTypeAtEx(node, true /*includeGlobalThis*/, container) + t := c.tryGetThisTypeAtEx(node, true /*includeGlobalThis*/, container, checkMode) if c.noImplicitThis { globalThisType := c.getTypeOfSymbol(c.globalThisSymbol) if t == globalThisType && capturedByArrowFunction { @@ -12311,7 +12311,7 @@ func (c *Checker) checkThisExpression(node *ast.Node) *Type { } func (c *Checker) tryGetThisTypeAt(node *ast.Node) *Type { - return c.tryGetThisTypeAtEx(node, true /*includeGlobalThis*/, nil /*container*/) + return c.tryGetThisTypeAtEx(node, true /*includeGlobalThis*/, nil /*container*/, CheckModeNormal) } func (c *Checker) TryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node) *Type { @@ -12319,10 +12319,10 @@ func (c *Checker) TryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, con if reparsed.Flags&ast.NodeFlagsJSDoc != 0 && reparsed.Flags&ast.NodeFlagsReparsed == 0 { return nil // Binder doesn't process non-reparsed JSDoc nodes } - return c.tryGetThisTypeAtEx(reparsed, includeGlobalThis, ast.GetReparsedNodeForNode(container)) + return c.tryGetThisTypeAtEx(reparsed, includeGlobalThis, ast.GetReparsedNodeForNode(container), CheckModeNormal) } -func (c *Checker) tryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node) *Type { +func (c *Checker) tryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node, checkMode CheckMode) *Type { if container == nil { container = c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) } @@ -12338,6 +12338,7 @@ func (c *Checker) tryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, con thisType = c.getContextualThisParameterType(container) } if thisType != nil { + thisType = c.getNarrowableTypeForReference(thisType, node, checkMode) return c.getFlowTypeOfReference(node, thisType) } } @@ -31880,7 +31881,7 @@ func (c *Checker) hasContextualTypeWithNoGenericTypes(node *ast.Node, checkMode // element's tag name, so we exclude that here to avoid circularities. // If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types, // as we want the type of a rest element to be generic when possible. - if (ast.IsIdentifier(node) || ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) && + if (ast.IsIdentifier(node) || node.Kind == ast.KindThisKeyword || ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) && !((ast.IsJsxOpeningElement(node.Parent) || ast.IsJsxSelfClosingElement(node.Parent)) && node.Parent.TagName() == node) { contextualType := c.getContextualType(node, core.IfElse(checkMode&CheckModeRestBindingElement != 0, ContextFlagsSkipBindingPatterns, ContextFlagsNone)) if contextualType != nil { diff --git a/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.errors.txt b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.errors.txt new file mode 100644 index 0000000000000..e2746633cb00a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.errors.txt @@ -0,0 +1,81 @@ +controlFlowGenericThis.ts(10,22): error TS2678: Type '"str"' is not comparable to type '"i32"'. +controlFlowGenericThis.ts(21,22): error TS2678: Type '"str"' is not comparable to type '"i32"'. +controlFlowGenericThis.ts(33,22): error TS2678: Type '"str"' is not comparable to type '"i32"'. + + +==== controlFlowGenericThis.ts (3 errors) ==== + type Type = I32 | Str; + interface Str { group: "none"; kind: "str"; } + interface I32 { group: "scalar"; kind: "i32"; } + + function testThis(this: T) { + switch (this.group) { + case "none": return; + case "scalar": + switch (this.kind) { + case "str": // Error, just as for an ordinary parameter. + ~~~~~ +!!! error TS2678: Type '"str"' is not comparable to type '"i32"'. + case "i32": + } + } + } + + function testParameter(o: T) { + switch (o.group) { + case "none": return; + case "scalar": + switch (o.kind) { + case "str": + ~~~~~ +!!! error TS2678: Type '"str"' is not comparable to type '"i32"'. + case "i32": + } + } + } + + function testAliasedThis(this: T) { + const o = this; + switch (o.group) { + case "none": return; + case "scalar": + switch (o.kind) { + case "str": + ~~~~~ +!!! error TS2678: Type '"str"' is not comparable to type '"i32"'. + case "i32": + } + } + } + + function testAccess(this: T): T { + if (this.group === "scalar") { + const kind: "i32" = this.kind; + const indexedKind: "i32" = this["kind"]; + const arrow = () => { + const kind: "i32" = this.kind; + }; + return this; + } + const kind: "str" = this.kind; + return this; + } + + function testAliasedAccess(this: T): T { + const o = this; + if (o.group === "scalar") { + const kind: "i32" = o.kind; + const indexedKind: "i32" = o["kind"]; + const arrow = () => { + const kind: "i32" = o.kind; + }; + return o; + } + const kind: "str" = o.kind; + return o; + } + + function testGenericIndex(this: T, key: K): T[K] { + return this[key]; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.symbols b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.symbols new file mode 100644 index 0000000000000..eb8c87267cc36 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.symbols @@ -0,0 +1,213 @@ +//// [tests/cases/compiler/controlFlowGenericThis.ts] //// + +=== controlFlowGenericThis.ts === +type Type = I32 | Str; +>Type : Symbol(Type, Decl(controlFlowGenericThis.ts, 0, 0)) +>I32 : Symbol(I32, Decl(controlFlowGenericThis.ts, 1, 45)) +>Str : Symbol(Str, Decl(controlFlowGenericThis.ts, 0, 22)) + +interface Str { group: "none"; kind: "str"; } +>Str : Symbol(Str, Decl(controlFlowGenericThis.ts, 0, 22)) +>group : Symbol(Str.group, Decl(controlFlowGenericThis.ts, 1, 15)) +>kind : Symbol(Str.kind, Decl(controlFlowGenericThis.ts, 1, 30)) + +interface I32 { group: "scalar"; kind: "i32"; } +>I32 : Symbol(I32, Decl(controlFlowGenericThis.ts, 1, 45)) +>group : Symbol(I32.group, Decl(controlFlowGenericThis.ts, 2, 15)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + +function testThis(this: T) { +>testThis : Symbol(testThis, Decl(controlFlowGenericThis.ts, 2, 47)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 4, 18)) +>Type : Symbol(Type, Decl(controlFlowGenericThis.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 4, 34)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 4, 18)) + + switch (this.group) { +>this.group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 4, 34)) +>group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) + + case "none": return; + case "scalar": + switch (this.kind) { +>this.kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 4, 34)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + case "str": // Error, just as for an ordinary parameter. + case "i32": + } + } +} + +function testParameter(o: T) { +>testParameter : Symbol(testParameter, Decl(controlFlowGenericThis.ts, 13, 1)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 15, 23)) +>Type : Symbol(Type, Decl(controlFlowGenericThis.ts, 0, 0)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 15, 39)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 15, 23)) + + switch (o.group) { +>o.group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 15, 39)) +>group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) + + case "none": return; + case "scalar": + switch (o.kind) { +>o.kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 15, 39)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + case "str": + case "i32": + } + } +} + +function testAliasedThis(this: T) { +>testAliasedThis : Symbol(testAliasedThis, Decl(controlFlowGenericThis.ts, 24, 1)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 26, 25)) +>Type : Symbol(Type, Decl(controlFlowGenericThis.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 26, 41)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 26, 25)) + + const o = this; +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 27, 9)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 26, 41)) + + switch (o.group) { +>o.group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 27, 9)) +>group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) + + case "none": return; + case "scalar": + switch (o.kind) { +>o.kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 27, 9)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + case "str": + case "i32": + } + } +} + +function testAccess(this: T): T { +>testAccess : Symbol(testAccess, Decl(controlFlowGenericThis.ts, 36, 1)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 38, 20)) +>Type : Symbol(Type, Decl(controlFlowGenericThis.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 38, 20)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 38, 20)) + + if (this.group === "scalar") { +>this.group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) +>group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) + + const kind: "i32" = this.kind; +>kind : Symbol(kind, Decl(controlFlowGenericThis.ts, 40, 13)) +>this.kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + const indexedKind: "i32" = this["kind"]; +>indexedKind : Symbol(indexedKind, Decl(controlFlowGenericThis.ts, 41, 13)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) +>"kind" : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + const arrow = () => { +>arrow : Symbol(arrow, Decl(controlFlowGenericThis.ts, 42, 13)) + + const kind: "i32" = this.kind; +>kind : Symbol(kind, Decl(controlFlowGenericThis.ts, 43, 17)) +>this.kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + }; + return this; +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) + } + const kind: "str" = this.kind; +>kind : Symbol(kind, Decl(controlFlowGenericThis.ts, 47, 9)) +>this.kind : Symbol(Str.kind, Decl(controlFlowGenericThis.ts, 1, 30)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) +>kind : Symbol(Str.kind, Decl(controlFlowGenericThis.ts, 1, 30)) + + return this; +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 38, 36)) +} + +function testAliasedAccess(this: T): T { +>testAliasedAccess : Symbol(testAliasedAccess, Decl(controlFlowGenericThis.ts, 49, 1)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 51, 27)) +>Type : Symbol(Type, Decl(controlFlowGenericThis.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 51, 43)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 51, 27)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 51, 27)) + + const o = this; +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 51, 43)) + + if (o.group === "scalar") { +>o.group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) +>group : Symbol(group, Decl(controlFlowGenericThis.ts, 2, 15), Decl(controlFlowGenericThis.ts, 1, 15)) + + const kind: "i32" = o.kind; +>kind : Symbol(kind, Decl(controlFlowGenericThis.ts, 54, 13)) +>o.kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + const indexedKind: "i32" = o["kind"]; +>indexedKind : Symbol(indexedKind, Decl(controlFlowGenericThis.ts, 55, 13)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) +>"kind" : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + const arrow = () => { +>arrow : Symbol(arrow, Decl(controlFlowGenericThis.ts, 56, 13)) + + const kind: "i32" = o.kind; +>kind : Symbol(kind, Decl(controlFlowGenericThis.ts, 57, 17)) +>o.kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThis.ts, 2, 32)) + + }; + return o; +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) + } + const kind: "str" = o.kind; +>kind : Symbol(kind, Decl(controlFlowGenericThis.ts, 61, 9)) +>o.kind : Symbol(Str.kind, Decl(controlFlowGenericThis.ts, 1, 30)) +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) +>kind : Symbol(Str.kind, Decl(controlFlowGenericThis.ts, 1, 30)) + + return o; +>o : Symbol(o, Decl(controlFlowGenericThis.ts, 52, 9)) +} + +function testGenericIndex(this: T, key: K): T[K] { +>testGenericIndex : Symbol(testGenericIndex, Decl(controlFlowGenericThis.ts, 63, 1)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 65, 26)) +>Type : Symbol(Type, Decl(controlFlowGenericThis.ts, 0, 0)) +>K : Symbol(K, Decl(controlFlowGenericThis.ts, 65, 41)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 65, 26)) +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 65, 61)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 65, 26)) +>key : Symbol(key, Decl(controlFlowGenericThis.ts, 65, 69)) +>K : Symbol(K, Decl(controlFlowGenericThis.ts, 65, 41)) +>T : Symbol(T, Decl(controlFlowGenericThis.ts, 65, 26)) +>K : Symbol(K, Decl(controlFlowGenericThis.ts, 65, 41)) + + return this[key]; +>this : Symbol(this, Decl(controlFlowGenericThis.ts, 65, 61)) +>key : Symbol(key, Decl(controlFlowGenericThis.ts, 65, 69)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.types b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.types new file mode 100644 index 0000000000000..9fb0cf80822d5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThis.types @@ -0,0 +1,214 @@ +//// [tests/cases/compiler/controlFlowGenericThis.ts] //// + +=== controlFlowGenericThis.ts === +type Type = I32 | Str; +>Type : Type + +interface Str { group: "none"; kind: "str"; } +>group : "none" +>kind : "str" + +interface I32 { group: "scalar"; kind: "i32"; } +>group : "scalar" +>kind : "i32" + +function testThis(this: T) { +>testThis : (this: T) => void +>this : T + + switch (this.group) { +>this.group : "none" | "scalar" +>this : Type +>group : "none" | "scalar" + + case "none": return; +>"none" : "none" + + case "scalar": +>"scalar" : "scalar" + + switch (this.kind) { +>this.kind : "i32" +>this : I32 +>kind : "i32" + + case "str": // Error, just as for an ordinary parameter. +>"str" : "str" + + case "i32": +>"i32" : "i32" + } + } +} + +function testParameter(o: T) { +>testParameter : (o: T) => void +>o : T + + switch (o.group) { +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" + + case "none": return; +>"none" : "none" + + case "scalar": +>"scalar" : "scalar" + + switch (o.kind) { +>o.kind : "i32" +>o : I32 +>kind : "i32" + + case "str": +>"str" : "str" + + case "i32": +>"i32" : "i32" + } + } +} + +function testAliasedThis(this: T) { +>testAliasedThis : (this: T) => void +>this : T + + const o = this; +>o : T +>this : T + + switch (o.group) { +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" + + case "none": return; +>"none" : "none" + + case "scalar": +>"scalar" : "scalar" + + switch (o.kind) { +>o.kind : "i32" +>o : I32 +>kind : "i32" + + case "str": +>"str" : "str" + + case "i32": +>"i32" : "i32" + } + } +} + +function testAccess(this: T): T { +>testAccess : (this: T) => T +>this : T + + if (this.group === "scalar") { +>this.group === "scalar" : boolean +>this.group : "none" | "scalar" +>this : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const kind: "i32" = this.kind; +>kind : "i32" +>this.kind : "i32" +>this : I32 +>kind : "i32" + + const indexedKind: "i32" = this["kind"]; +>indexedKind : "i32" +>this["kind"] : "i32" +>this : I32 +>"kind" : "kind" + + const arrow = () => { +>arrow : () => void +>() => { const kind: "i32" = this.kind; } : () => void + + const kind: "i32" = this.kind; +>kind : "i32" +>this.kind : "i32" +>this : I32 +>kind : "i32" + + }; + return this; +>this : T + } + const kind: "str" = this.kind; +>kind : "str" +>this.kind : "str" +>this : Str +>kind : "str" + + return this; +>this : T +} + +function testAliasedAccess(this: T): T { +>testAliasedAccess : (this: T) => T +>this : T + + const o = this; +>o : T +>this : T + + if (o.group === "scalar") { +>o.group === "scalar" : boolean +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const kind: "i32" = o.kind; +>kind : "i32" +>o.kind : "i32" +>o : I32 +>kind : "i32" + + const indexedKind: "i32" = o["kind"]; +>indexedKind : "i32" +>o["kind"] : "i32" +>o : I32 +>"kind" : "kind" + + const arrow = () => { +>arrow : () => void +>() => { const kind: "i32" = o.kind; } : () => void + + const kind: "i32" = o.kind; +>kind : "i32" +>o.kind : "i32" +>o : I32 +>kind : "i32" + + }; + return o; +>o : T + } + const kind: "str" = o.kind; +>kind : "str" +>o.kind : "str" +>o : Str +>kind : "str" + + return o; +>o : T +} + +function testGenericIndex(this: T, key: K): T[K] { +>testGenericIndex : (this: T, key: K) => T[K] +>this : T +>key : K + + return this[key]; +>this[key] : T[K] +>this : T +>key : K +} + diff --git a/tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.symbols b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.symbols new file mode 100644 index 0000000000000..02b40a001de12 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.symbols @@ -0,0 +1,254 @@ +//// [tests/cases/compiler/controlFlowGenericThisContextual.ts] //// + +=== controlFlowGenericThisContextual.ts === +type Type = I32 | Str; +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>Str : Symbol(Str, Decl(controlFlowGenericThisContextual.ts, 0, 22)) + +interface Str { group: "none"; kind: "str"; } +>Str : Symbol(Str, Decl(controlFlowGenericThisContextual.ts, 0, 22)) +>group : Symbol(Str.group, Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>kind : Symbol(Str.kind, Decl(controlFlowGenericThisContextual.ts, 1, 30)) + +interface I32 { group: "scalar"; kind: "i32"; } +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>group : Symbol(I32.group, Decl(controlFlowGenericThisContextual.ts, 2, 15)) +>kind : Symbol(I32.kind, Decl(controlFlowGenericThisContextual.ts, 2, 32)) + +declare function takeI32(value: I32): void; +>takeI32 : Symbol(takeI32, Decl(controlFlowGenericThisContextual.ts, 2, 47)) +>value : Symbol(value, Decl(controlFlowGenericThisContextual.ts, 4, 25)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + +declare function inferKind(value: { kind: T }): T; +>inferKind : Symbol(inferKind, Decl(controlFlowGenericThisContextual.ts, 4, 43)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 5, 27)) +>value : Symbol(value, Decl(controlFlowGenericThisContextual.ts, 5, 30)) +>kind : Symbol(kind, Decl(controlFlowGenericThisContextual.ts, 5, 38)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 5, 27)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 5, 27)) + +function testThis(this: T): I32 | undefined { +>testThis : Symbol(testThis, Decl(controlFlowGenericThisContextual.ts, 5, 53)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 7, 18)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 7, 34)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 7, 18)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + if (this.group === "scalar") { +>this.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 7, 34)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + const value: I32 = this; +>value : Symbol(value, Decl(controlFlowGenericThisContextual.ts, 9, 13)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 7, 34)) + + takeI32(this); +>takeI32 : Symbol(takeI32, Decl(controlFlowGenericThisContextual.ts, 2, 47)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 7, 34)) + + this satisfies I32; +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 7, 34)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + const arrow = (): I32 => this; +>arrow : Symbol(arrow, Decl(controlFlowGenericThisContextual.ts, 12, 13)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 7, 34)) + + return this; +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 7, 34)) + } +} + +function testParameter(o: T): I32 | undefined { +>testParameter : Symbol(testParameter, Decl(controlFlowGenericThisContextual.ts, 15, 1)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 17, 23)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 17, 39)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 17, 23)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + if (o.group === "scalar") { +>o.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 17, 39)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + const value: I32 = o; +>value : Symbol(value, Decl(controlFlowGenericThisContextual.ts, 19, 13)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 17, 39)) + + takeI32(o); +>takeI32 : Symbol(takeI32, Decl(controlFlowGenericThisContextual.ts, 2, 47)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 17, 39)) + + o satisfies I32; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 17, 39)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + const arrow = (): I32 => o; +>arrow : Symbol(arrow, Decl(controlFlowGenericThisContextual.ts, 22, 13)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 17, 39)) + + return o; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 17, 39)) + } +} + +function testAliasedThis(this: T): I32 | undefined { +>testAliasedThis : Symbol(testAliasedThis, Decl(controlFlowGenericThisContextual.ts, 25, 1)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 27, 25)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 27, 41)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 27, 25)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + const o = this; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 28, 9)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 27, 41)) + + if (o.group === "scalar") { +>o.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 28, 9)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + const value: I32 = o; +>value : Symbol(value, Decl(controlFlowGenericThisContextual.ts, 30, 13)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 28, 9)) + + takeI32(o); +>takeI32 : Symbol(takeI32, Decl(controlFlowGenericThisContextual.ts, 2, 47)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 28, 9)) + + o satisfies I32; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 28, 9)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + const arrow = (): I32 => o; +>arrow : Symbol(arrow, Decl(controlFlowGenericThisContextual.ts, 33, 13)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 28, 9)) + + return o; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 28, 9)) + } +} + +function testInferenceThis(this: T) { +>testInferenceThis : Symbol(testInferenceThis, Decl(controlFlowGenericThisContextual.ts, 36, 1)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 38, 27)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 38, 43)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 38, 27)) + + if (this.group === "scalar") { +>this.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 38, 43)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + const kind = inferKind(this); +>kind : Symbol(kind, Decl(controlFlowGenericThisContextual.ts, 40, 13)) +>inferKind : Symbol(inferKind, Decl(controlFlowGenericThisContextual.ts, 4, 43)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 38, 43)) + } +} + +function testInferenceParameter(o: T) { +>testInferenceParameter : Symbol(testInferenceParameter, Decl(controlFlowGenericThisContextual.ts, 42, 1)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 44, 32)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 44, 48)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 44, 32)) + + if (o.group === "scalar") { +>o.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 44, 48)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + const kind = inferKind(o); +>kind : Symbol(kind, Decl(controlFlowGenericThisContextual.ts, 46, 13)) +>inferKind : Symbol(inferKind, Decl(controlFlowGenericThisContextual.ts, 4, 43)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 44, 48)) + } +} + +function testInferenceAliasedThis(this: T) { +>testInferenceAliasedThis : Symbol(testInferenceAliasedThis, Decl(controlFlowGenericThisContextual.ts, 48, 1)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 50, 34)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 50, 50)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 50, 34)) + + const o = this; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 51, 9)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 50, 50)) + + if (o.group === "scalar") { +>o.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 51, 9)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + const kind = inferKind(o); +>kind : Symbol(kind, Decl(controlFlowGenericThisContextual.ts, 53, 13)) +>inferKind : Symbol(inferKind, Decl(controlFlowGenericThisContextual.ts, 4, 43)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 51, 9)) + } +} + +const contextual: { method(this: T): I32 | undefined } = { +>contextual : Symbol(contextual, Decl(controlFlowGenericThisContextual.ts, 57, 5)) +>method : Symbol(method, Decl(controlFlowGenericThisContextual.ts, 57, 19)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 57, 27)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 57, 43)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 57, 27)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + method() { +>method : Symbol(method, Decl(controlFlowGenericThisContextual.ts, 57, 74)) + + if (this.group === "scalar") { +>this.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 57, 43)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + return this; +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 57, 43)) + } + }, +}; + +const contextualAliased: { method(this: T): I32 | undefined } = { +>contextualAliased : Symbol(contextualAliased, Decl(controlFlowGenericThisContextual.ts, 65, 5)) +>method : Symbol(method, Decl(controlFlowGenericThisContextual.ts, 65, 26)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 65, 34)) +>Type : Symbol(Type, Decl(controlFlowGenericThisContextual.ts, 0, 0)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 65, 50)) +>T : Symbol(T, Decl(controlFlowGenericThisContextual.ts, 65, 34)) +>I32 : Symbol(I32, Decl(controlFlowGenericThisContextual.ts, 1, 45)) + + method() { +>method : Symbol(method, Decl(controlFlowGenericThisContextual.ts, 65, 81)) + + const o = this; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 67, 13)) +>this : Symbol(this, Decl(controlFlowGenericThisContextual.ts, 65, 50)) + + if (o.group === "scalar") { +>o.group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 67, 13)) +>group : Symbol(group, Decl(controlFlowGenericThisContextual.ts, 2, 15), Decl(controlFlowGenericThisContextual.ts, 1, 15)) + + return o; +>o : Symbol(o, Decl(controlFlowGenericThisContextual.ts, 67, 13)) + } + }, +}; + diff --git a/tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.types b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.types new file mode 100644 index 0000000000000..13e214f880675 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/controlFlowGenericThisContextual.types @@ -0,0 +1,238 @@ +//// [tests/cases/compiler/controlFlowGenericThisContextual.ts] //// + +=== controlFlowGenericThisContextual.ts === +type Type = I32 | Str; +>Type : Type + +interface Str { group: "none"; kind: "str"; } +>group : "none" +>kind : "str" + +interface I32 { group: "scalar"; kind: "i32"; } +>group : "scalar" +>kind : "i32" + +declare function takeI32(value: I32): void; +>takeI32 : (value: I32) => void +>value : I32 + +declare function inferKind(value: { kind: T }): T; +>inferKind : (value: { kind: T; }) => T +>value : { kind: T; } +>kind : T + +function testThis(this: T): I32 | undefined { +>testThis : (this: T) => I32 | undefined +>this : T + + if (this.group === "scalar") { +>this.group === "scalar" : boolean +>this.group : "none" | "scalar" +>this : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const value: I32 = this; +>value : I32 +>this : I32 + + takeI32(this); +>takeI32(this) : void +>takeI32 : (value: I32) => void +>this : I32 + + this satisfies I32; +>this satisfies I32 : I32 +>this : I32 + + const arrow = (): I32 => this; +>arrow : () => I32 +>(): I32 => this : () => I32 +>this : I32 + + return this; +>this : I32 + } +} + +function testParameter(o: T): I32 | undefined { +>testParameter : (o: T) => I32 | undefined +>o : T + + if (o.group === "scalar") { +>o.group === "scalar" : boolean +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const value: I32 = o; +>value : I32 +>o : I32 + + takeI32(o); +>takeI32(o) : void +>takeI32 : (value: I32) => void +>o : I32 + + o satisfies I32; +>o satisfies I32 : I32 +>o : I32 + + const arrow = (): I32 => o; +>arrow : () => I32 +>(): I32 => o : () => I32 +>o : I32 + + return o; +>o : I32 + } +} + +function testAliasedThis(this: T): I32 | undefined { +>testAliasedThis : (this: T) => I32 | undefined +>this : T + + const o = this; +>o : T +>this : T + + if (o.group === "scalar") { +>o.group === "scalar" : boolean +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const value: I32 = o; +>value : I32 +>o : I32 + + takeI32(o); +>takeI32(o) : void +>takeI32 : (value: I32) => void +>o : I32 + + o satisfies I32; +>o satisfies I32 : I32 +>o : I32 + + const arrow = (): I32 => o; +>arrow : () => I32 +>(): I32 => o : () => I32 +>o : I32 + + return o; +>o : I32 + } +} + +function testInferenceThis(this: T) { +>testInferenceThis : (this: T) => void +>this : T + + if (this.group === "scalar") { +>this.group === "scalar" : boolean +>this.group : "none" | "scalar" +>this : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const kind = inferKind(this); +>kind : "i32" | "str" +>inferKind(this) : "i32" | "str" +>inferKind : (value: { kind: T_1; }) => T_1 +>this : I32 + } +} + +function testInferenceParameter(o: T) { +>testInferenceParameter : (o: T) => void +>o : T + + if (o.group === "scalar") { +>o.group === "scalar" : boolean +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const kind = inferKind(o); +>kind : "i32" | "str" +>inferKind(o) : "i32" | "str" +>inferKind : (value: { kind: T_1; }) => T_1 +>o : I32 + } +} + +function testInferenceAliasedThis(this: T) { +>testInferenceAliasedThis : (this: T) => void +>this : T + + const o = this; +>o : T +>this : T + + if (o.group === "scalar") { +>o.group === "scalar" : boolean +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + const kind = inferKind(o); +>kind : "i32" | "str" +>inferKind(o) : "i32" | "str" +>inferKind : (value: { kind: T_1; }) => T_1 +>o : I32 + } +} + +const contextual: { method(this: T): I32 | undefined } = { +>contextual : { method(this: T): I32 | undefined; } +>method : (this: T) => I32 | undefined +>this : T +>{ method() { if (this.group === "scalar") { return this; } },} : { method(this: T): I32 | undefined; } + + method() { +>method : (this: T) => I32 | undefined + + if (this.group === "scalar") { +>this.group === "scalar" : boolean +>this.group : "none" | "scalar" +>this : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + return this; +>this : I32 + } + }, +}; + +const contextualAliased: { method(this: T): I32 | undefined } = { +>contextualAliased : { method(this: T): I32 | undefined; } +>method : (this: T) => I32 | undefined +>this : T +>{ method() { const o = this; if (o.group === "scalar") { return o; } },} : { method(this: T): I32 | undefined; } + + method() { +>method : (this: T) => I32 | undefined + + const o = this; +>o : T +>this : T + + if (o.group === "scalar") { +>o.group === "scalar" : boolean +>o.group : "none" | "scalar" +>o : Type +>group : "none" | "scalar" +>"scalar" : "scalar" + + return o; +>o : I32 + } + }, +}; + diff --git a/tsc/testdata/tests/cases/compiler/controlFlowGenericThis.ts b/tsc/testdata/tests/cases/compiler/controlFlowGenericThis.ts new file mode 100644 index 0000000000000..4dc0d6fca56e8 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/controlFlowGenericThis.ts @@ -0,0 +1,71 @@ +// @strict: true +// @noEmit: true + +type Type = I32 | Str; +interface Str { group: "none"; kind: "str"; } +interface I32 { group: "scalar"; kind: "i32"; } + +function testThis(this: T) { + switch (this.group) { + case "none": return; + case "scalar": + switch (this.kind) { + case "str": // Error, just as for an ordinary parameter. + case "i32": + } + } +} + +function testParameter(o: T) { + switch (o.group) { + case "none": return; + case "scalar": + switch (o.kind) { + case "str": + case "i32": + } + } +} + +function testAliasedThis(this: T) { + const o = this; + switch (o.group) { + case "none": return; + case "scalar": + switch (o.kind) { + case "str": + case "i32": + } + } +} + +function testAccess(this: T): T { + if (this.group === "scalar") { + const kind: "i32" = this.kind; + const indexedKind: "i32" = this["kind"]; + const arrow = () => { + const kind: "i32" = this.kind; + }; + return this; + } + const kind: "str" = this.kind; + return this; +} + +function testAliasedAccess(this: T): T { + const o = this; + if (o.group === "scalar") { + const kind: "i32" = o.kind; + const indexedKind: "i32" = o["kind"]; + const arrow = () => { + const kind: "i32" = o.kind; + }; + return o; + } + const kind: "str" = o.kind; + return o; +} + +function testGenericIndex(this: T, key: K): T[K] { + return this[key]; +} diff --git a/tsc/testdata/tests/cases/compiler/controlFlowGenericThisContextual.ts b/tsc/testdata/tests/cases/compiler/controlFlowGenericThisContextual.ts new file mode 100644 index 0000000000000..19ddda16c6451 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/controlFlowGenericThisContextual.ts @@ -0,0 +1,76 @@ +// @strict: true +// @noEmit: true + +type Type = I32 | Str; +interface Str { group: "none"; kind: "str"; } +interface I32 { group: "scalar"; kind: "i32"; } + +declare function takeI32(value: I32): void; +declare function inferKind(value: { kind: T }): T; + +function testThis(this: T): I32 | undefined { + if (this.group === "scalar") { + const value: I32 = this; + takeI32(this); + this satisfies I32; + const arrow = (): I32 => this; + return this; + } +} + +function testParameter(o: T): I32 | undefined { + if (o.group === "scalar") { + const value: I32 = o; + takeI32(o); + o satisfies I32; + const arrow = (): I32 => o; + return o; + } +} + +function testAliasedThis(this: T): I32 | undefined { + const o = this; + if (o.group === "scalar") { + const value: I32 = o; + takeI32(o); + o satisfies I32; + const arrow = (): I32 => o; + return o; + } +} + +function testInferenceThis(this: T) { + if (this.group === "scalar") { + const kind = inferKind(this); + } +} + +function testInferenceParameter(o: T) { + if (o.group === "scalar") { + const kind = inferKind(o); + } +} + +function testInferenceAliasedThis(this: T) { + const o = this; + if (o.group === "scalar") { + const kind = inferKind(o); + } +} + +const contextual: { method(this: T): I32 | undefined } = { + method() { + if (this.group === "scalar") { + return this; + } + }, +}; + +const contextualAliased: { method(this: T): I32 | undefined } = { + method() { + const o = this; + if (o.group === "scalar") { + return o; + } + }, +};