microsoft / microsoft/TypeScript

Narrowing of generic this is inconsistent with variable narrowing

Open
#64,186 0 comments 0 reactions 0 assignees View on GitHub
Possible Improvement
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

### 🔎 Search Terms

generic union constraint narrowing this

### 🕗 Version & Regression Information

- This is the behavior in every version I tried

### ⏯ Playground Link

https://www.typescriptlang.org/play/?noImplicitReturns=false#code/C4TwDgpgBAKu0F4oEkDMAmKAfKBlYATgNwBQAlgHbAQEBmAhgMbT4FQDeJUUA5gQPYBXMAC4oAIgr8KEcaW4BrSgBMx4gM6E5JAL7kqNBsxQYOXXgOFr1jegBt6BbYpVqyGbXpLKIjBwWhaQQpGYDJpKGB6BQg0dAAKADd7QQgxOIBKMUT+MmVSHz9HQODQ8IooSloaAGkVAB4YAD4klLSOKCUKVVgoHSzYUhIgkLCI6k0YAAsydUaoCAAPam71WHgW4Bn1MRgMs251AHcyYEYpqHit2YA6PiEwfc5ublt1aElpWRFzF6gA4CCAgUeQvN4fGz2RziH5-Q4nM4XK7bG5dZRPX5w8ESTROMQAenxUCkCwIAgIAH5MX9seJ3OgYdS+uY9F4RmVxhBNAAFRz0AC2EGoBHmSxWyjWcEgLX4uwx8NO50u-DulkeBzB9HeEikMkZcP+QqBIMxtMh-n1cOOiqRKrR8oNUDNWgJRJo5IAhEzXlqPvTLX89NxWSRhqUxhUJsAAIJ2Mi+5TTWai5YQVbraXI2ZyjWMaSaKD8KBIa7qUHWxHK1UPB0+7WfPWwuEA42gusQ2wWpt-CtK+J2lS1rG+nEuqCE0me71Okd0jzdl5B5ls8PlSJc4BJ9ToFPiyUbLM7WADOLYKDBHy0SgQZQasi0S6l6vCYsIJAaTvQod5igF5J2VJ0lMEttiIcc3TJfgCExKIYjiQ8MjAid3SgmDtigdR6DCdQry5Ex0CQiDyVNfNgCgRwBCOYtLhPYCmkiUDwMnVC-hbYEGNmQjmOg4NdFDdkI3XHk+UFYUdxgBZU3TKUIBlOUgMwHALwgK8ZFvZ5KgfftnzAV933NL8NVeUioH-QD8Oo-guP4BQYOiWIMH7RCmJszEi0w7DcLWOJrNsmkTIo-gqKQeJaMwBB6Ksly-JeNiKiiidXN4ldRjXKNY3jd5E22cTJL3DNZMPeSLKU7oVOvdTzB-AsixAzjzHvKt7hfN99M-JxvxMsz2lPJAEqJJKXlghyEn4ZzEpi7h3Kw2YvPw3ySN-MjAuCmiFOLSKFtYo12P6wsYpDIA

### 💻 Code

```ts
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) {
switch (this.group) {
case "none":
return;
case "scalar":
switch (this.kind) {
case "str": // no error?
case "i32":
}
}
}

function testParameter(o: T) {
switch (o.group) {
case "none":
return;
case "scalar":
switch (o.kind) {
case "str": // error!
case "i32":
}
}
}

function testAliasedThis(this: T) {
const o = this;
switch (o.group) {
case "none":
return;
case "scalar":
switch (o.kind) {
case "str": // error!
case "i32":
}
}
}

function testThis2(this: T): I32 | undefined {
if (this.group === "scalar") {
const value: I32 = this; // error
takeI32(this); // error
this satisfies I32; // error
const arrow = (): I32 => this; // error
return this; // error
}
}

function testParameter2(o: T): I32 | undefined {
if (o.group === "scalar") {
const value: I32 = o; // ok
takeI32(o); // ok
o satisfies I32; // ok
const arrow = (): I32 => o; // ok
return o; // ok
}
}

function testAliasedThis2(this: T): I32 | undefined {
const o = this;
if (o.group === "scalar") {
const value: I32 = o; // ok
takeI32(o); // ok
o satisfies I32; // ok
const arrow = (): I32 => o; // ok
return o; // ok
}
}

```

### 🙁 Actual behavior

The errors (or lack of them) are inconsistent between narrowing `this` directly vs narrowing an "aliased" `this` (`const alias = this;`)

### 🙂 Expected behavior

They should be consistent

### Additional information about the issue

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the linked TypeScript Playground and reduce the provided generic-union examples for direct this, aliased this, and parameter narrowing. No repository file or test is identified in the issue, so locate the compiler narrowing entry point and add a regression test covering the shown cases. Done means direct and aliased this narrowing produce consistent diagnostics.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.