Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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*/)
Expand Down Expand Up @@ -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 {
Expand All @@ -12311,18 +12311,18 @@ 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 {
reparsed := ast.GetReparsedNodeForNode(node)
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*/)
}
Expand All @@ -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)
}
}
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T extends Type>(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<T extends Type>(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<T extends Type>(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<T extends Type>(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<T extends Type>(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<T extends Type, K extends keyof T>(this: T, key: K): T[K] {
return this[key];
}

Original file line number Diff line number Diff line change
@@ -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<T extends Type>(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<T extends Type>(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<T extends Type>(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<T extends Type>(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<T extends Type>(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<T extends Type, K extends keyof T>(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))
}

Loading