Treat this as a constant reference for purpose of narrowing using aliased expressions (#54347)

This commit is contained in:
Mateusz Burzyński 2023-06-02 01:44:53 +02:00 committed by GitHub
parent d49a28534a
commit 2fb0267630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 184 additions and 19 deletions

View File

@ -26457,6 +26457,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function isConstantReference(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword:
return true;
case SyntaxKind.Identifier:
if (!isThisInTypeQuery(node)) {
const symbol = getResolvedSymbol(node as Identifier);

View File

@ -22,17 +22,13 @@ tests/cases/conformance/controlFlow/controlFlowAliasing.ts(154,19): error TS2339
Property 'foo' does not exist on type '{ kind: "bar"; bar: number; }'.
tests/cases/conformance/controlFlow/controlFlowAliasing.ts(157,19): error TS2339: Property 'bar' does not exist on type '{ kind: "foo"; foo: string; } | { kind: "bar"; bar: number; }'.
Property 'bar' does not exist on type '{ kind: "foo"; foo: string; }'.
tests/cases/conformance/controlFlow/controlFlowAliasing.ts(219,13): error TS2322: Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/controlFlow/controlFlowAliasing.ts(232,13): error TS2322: Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/controlFlow/controlFlowAliasing.ts(233,13): error TS2322: Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/controlFlow/controlFlowAliasing.ts(280,5): error TS2448: Block-scoped variable 'a' used before its declaration.
tests/cases/conformance/controlFlow/controlFlowAliasing.ts(280,5): error TS2454: Variable 'a' is used before being assigned.
==== tests/cases/conformance/controlFlow/controlFlowAliasing.ts (17 errors) ====
==== tests/cases/conformance/controlFlow/controlFlowAliasing.ts (15 errors) ====
// Narrowing by aliased conditional expressions
function f10(x: string | number) {
@ -288,9 +284,6 @@ tests/cases/conformance/controlFlow/controlFlowAliasing.ts(280,5): error TS2454:
if (thisX_isString && xIsString) {
let s: string;
s = this.x;
~
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
s = x;
}
}
@ -304,9 +297,6 @@ tests/cases/conformance/controlFlow/controlFlowAliasing.ts(280,5): error TS2454:
// Some narrowings may be invalidated due to later assignments.
let s: string;
s = this.x;
~
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
s = x;
~
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
@ -365,4 +355,22 @@ tests/cases/conformance/controlFlow/controlFlowAliasing.ts(280,5): error TS2454:
!!! error TS2454: Variable 'a' is used before being assigned.
const a = obj.fn();
// repro from https://github.com/microsoft/TypeScript/issues/53267
class Utils {
static isDefined<T>(value: T): value is NonNullable<T> {
return value != null;
}
}
class A53267 {
public readonly testNumber: number | undefined;
foo() {
const isNumber = Utils.isDefined(this.testNumber);
if (isNumber) {
const x: number = this.testNumber;
}
}
}

View File

@ -281,7 +281,25 @@ const obj = {
if (a) { }
const a = obj.fn();
// repro from https://github.com/microsoft/TypeScript/issues/53267
class Utils {
static isDefined<T>(value: T): value is NonNullable<T> {
return value != null;
}
}
class A53267 {
public readonly testNumber: number | undefined;
foo() {
const isNumber = Utils.isDefined(this.testNumber);
if (isNumber) {
const x: number = this.testNumber;
}
}
}
//// [controlFlowAliasing.js]
"use strict";
@ -538,6 +556,26 @@ var obj = {
};
if (a) { }
var a = obj.fn();
// repro from https://github.com/microsoft/TypeScript/issues/53267
var Utils = /** @class */ (function () {
function Utils() {
}
Utils.isDefined = function (value) {
return value != null;
};
return Utils;
}());
var A53267 = /** @class */ (function () {
function A53267() {
}
A53267.prototype.foo = function () {
var isNumber = Utils.isDefined(this.testNumber);
if (isNumber) {
var x = this.testNumber;
}
};
return A53267;
}());
//// [controlFlowAliasing.d.ts]
@ -677,3 +715,10 @@ declare const obj: {
fn: () => boolean;
};
declare const a: boolean;
declare class Utils {
static isDefined<T>(value: T): value is NonNullable<T>;
}
declare class A53267 {
readonly testNumber: number | undefined;
foo(): void;
}

View File

@ -791,3 +791,50 @@ const a = obj.fn();
>obj : Symbol(obj, Decl(controlFlowAliasing.ts, 275, 5))
>fn : Symbol(fn, Decl(controlFlowAliasing.ts, 275, 13))
// repro from https://github.com/microsoft/TypeScript/issues/53267
class Utils {
>Utils : Symbol(Utils, Decl(controlFlowAliasing.ts, 281, 19))
static isDefined<T>(value: T): value is NonNullable<T> {
>isDefined : Symbol(Utils.isDefined, Decl(controlFlowAliasing.ts, 284, 13))
>T : Symbol(T, Decl(controlFlowAliasing.ts, 285, 19))
>value : Symbol(value, Decl(controlFlowAliasing.ts, 285, 22))
>T : Symbol(T, Decl(controlFlowAliasing.ts, 285, 19))
>value : Symbol(value, Decl(controlFlowAliasing.ts, 285, 22))
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(controlFlowAliasing.ts, 285, 19))
return value != null;
>value : Symbol(value, Decl(controlFlowAliasing.ts, 285, 22))
}
}
class A53267 {
>A53267 : Symbol(A53267, Decl(controlFlowAliasing.ts, 288, 1))
public readonly testNumber: number | undefined;
>testNumber : Symbol(A53267.testNumber, Decl(controlFlowAliasing.ts, 290, 14))
foo() {
>foo : Symbol(A53267.foo, Decl(controlFlowAliasing.ts, 291, 49))
const isNumber = Utils.isDefined(this.testNumber);
>isNumber : Symbol(isNumber, Decl(controlFlowAliasing.ts, 294, 9))
>Utils.isDefined : Symbol(Utils.isDefined, Decl(controlFlowAliasing.ts, 284, 13))
>Utils : Symbol(Utils, Decl(controlFlowAliasing.ts, 281, 19))
>isDefined : Symbol(Utils.isDefined, Decl(controlFlowAliasing.ts, 284, 13))
>this.testNumber : Symbol(A53267.testNumber, Decl(controlFlowAliasing.ts, 290, 14))
>this : Symbol(A53267, Decl(controlFlowAliasing.ts, 288, 1))
>testNumber : Symbol(A53267.testNumber, Decl(controlFlowAliasing.ts, 290, 14))
if (isNumber) {
>isNumber : Symbol(isNumber, Decl(controlFlowAliasing.ts, 294, 9))
const x: number = this.testNumber;
>x : Symbol(x, Decl(controlFlowAliasing.ts, 297, 11))
>this.testNumber : Symbol(A53267.testNumber, Decl(controlFlowAliasing.ts, 290, 14))
>this : Symbol(A53267, Decl(controlFlowAliasing.ts, 288, 1))
>testNumber : Symbol(A53267.testNumber, Decl(controlFlowAliasing.ts, 290, 14))
}
}
}

View File

@ -731,11 +731,11 @@ class C10 {
>s : string
s = this.x;
>s = this.x : string | number
>s = this.x : string
>s : string
>this.x : string | number
>this.x : string
>this : this
>x : string | number
>x : string
s = x;
>s = x : string
@ -777,11 +777,11 @@ class C11 {
>s : string
s = this.x;
>s = this.x : string | number
>s = this.x : string
>s : string
>this.x : string | number
>this.x : string
>this : this
>x : string | number
>x : string
s = x;
>s = x : string | number
@ -918,3 +918,47 @@ const a = obj.fn();
>obj : { fn: () => boolean; }
>fn : () => boolean
// repro from https://github.com/microsoft/TypeScript/issues/53267
class Utils {
>Utils : Utils
static isDefined<T>(value: T): value is NonNullable<T> {
>isDefined : <T>(value: T) => value is NonNullable<T>
>value : T
return value != null;
>value != null : boolean
>value : T
}
}
class A53267 {
>A53267 : A53267
public readonly testNumber: number | undefined;
>testNumber : number | undefined
foo() {
>foo : () => void
const isNumber = Utils.isDefined(this.testNumber);
>isNumber : boolean
>Utils.isDefined(this.testNumber) : boolean
>Utils.isDefined : <T>(value: T) => value is NonNullable<T>
>Utils : typeof Utils
>isDefined : <T>(value: T) => value is NonNullable<T>
>this.testNumber : number | undefined
>this : this
>testNumber : number | undefined
if (isNumber) {
>isNumber : boolean
const x: number = this.testNumber;
>x : number
>this.testNumber : number
>this : this
>testNumber : number
}
}
}

View File

@ -283,3 +283,22 @@ const obj = {
if (a) { }
const a = obj.fn();
// repro from https://github.com/microsoft/TypeScript/issues/53267
class Utils {
static isDefined<T>(value: T): value is NonNullable<T> {
return value != null;
}
}
class A53267 {
public readonly testNumber: number | undefined;
foo() {
const isNumber = Utils.isDefined(this.testNumber);
if (isNumber) {
const x: number = this.testNumber;
}
}
}