Fixed control flow Analysis of aliased discriminants with parenthesized initializers (#61788)

This commit is contained in:
Mateusz Burzyński 2025-11-11 20:41:36 +01:00 committed by GitHub
parent 48244d89f8
commit 4a957b74ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 1002 additions and 10 deletions

View File

@ -29379,26 +29379,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const symbol = getResolvedSymbol(expr);
if (isConstantVariable(symbol)) {
const declaration = symbol.valueDeclaration!;
let initializer = getCandidateVariableDeclarationInitializer(declaration);
// Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
if (
isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer) &&
isMatchingReference(reference, declaration.initializer.expression)
) {
return declaration.initializer;
if (initializer && isAccessExpression(initializer) && isMatchingReference(reference, initializer.expression)) {
return initializer;
}
// Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
if (isBindingElement(declaration) && !declaration.initializer) {
const parent = declaration.parent.parent;
if (
isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer)) &&
isMatchingReference(reference, parent.initializer)
) {
initializer = getCandidateVariableDeclarationInitializer(declaration.parent.parent);
if (initializer && (isIdentifier(initializer) || isAccessExpression(initializer)) && isMatchingReference(reference, initializer)) {
return declaration;
}
}
}
}
return undefined;
function getCandidateVariableDeclarationInitializer(node: Node) {
return isVariableDeclaration(node) && !node.type && node.initializer ? skipParentheses(node.initializer) : undefined;
}
}
function getDiscriminantPropertyAccess(expr: Expression, computedType: Type) {

View File

@ -0,0 +1,292 @@
//// [tests/cases/conformance/controlFlow/controlFlowAliasing2.ts] ////
=== controlFlowAliasing2.ts ===
// https://github.com/microsoft/TypeScript/issues/61784
type Test = TestA | TestB;
>Test : Symbol(Test, Decl(controlFlowAliasing2.ts, 0, 0))
>TestA : Symbol(TestA, Decl(controlFlowAliasing2.ts, 2, 26))
>TestB : Symbol(TestB, Decl(controlFlowAliasing2.ts, 7, 1))
interface TestA {
>TestA : Symbol(TestA, Decl(controlFlowAliasing2.ts, 2, 26))
type: 'a';
>type : Symbol(TestA.type, Decl(controlFlowAliasing2.ts, 4, 17))
name: string;
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
interface TestB {
>TestB : Symbol(TestB, Decl(controlFlowAliasing2.ts, 7, 1))
type: 'b';
>type : Symbol(TestB.type, Decl(controlFlowAliasing2.ts, 9, 17))
value: number;
>value : Symbol(TestB.value, Decl(controlFlowAliasing2.ts, 10, 12))
}
function _tcb1(this: { test: Test }) {
>_tcb1 : Symbol(_tcb1, Decl(controlFlowAliasing2.ts, 12, 1))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 14, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>Test : Symbol(Test, Decl(controlFlowAliasing2.ts, 0, 0))
// TS generated by Angular's Type Check block
const _t1 = (((((this).test)).type));
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 16, 7))
>(((this).test)).type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 14, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
if (_t1 === "a") {
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 16, 7))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 14, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above, without the parenthesis
const _t2 = this.test.type;
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 22, 7))
>this.test.type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 14, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
if (_t2 === "a") {
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 22, 7))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 14, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above without parenthesis at both places
const testType = this.test.type;
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 28, 7))
>this.test.type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 14, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
if (testType === "a") {
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 28, 7))
this.test.name;
>this.test.name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 14, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 14, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
}
function _tcb2(this: { test: Test }) {
>_tcb2 : Symbol(_tcb2, Decl(controlFlowAliasing2.ts, 32, 1))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 34, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>Test : Symbol(Test, Decl(controlFlowAliasing2.ts, 0, 0))
// TS generated by Angular's Type Check block
const _t1 = (((((this).test)).type));
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 36, 7))
>(((this).test)).type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 34, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
if ("a" === _t1) {
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 36, 7))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 34, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above, without the parenthesis
const _t2 = this.test.type;
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 42, 7))
>this.test.type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 34, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
if ("a" === _t2) {
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 42, 7))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 34, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above without parenthesis at both places
const testType = this.test.type;
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 48, 7))
>this.test.type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 34, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
if ("a" === testType) {
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 48, 7))
this.test.name;
>this.test.name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 34, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 34, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
}
function _tcb3(this: { test: Test }) {
>_tcb3 : Symbol(_tcb3, Decl(controlFlowAliasing2.ts, 52, 1))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 54, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>Test : Symbol(Test, Decl(controlFlowAliasing2.ts, 0, 0))
const { type: _t1 } = (((((this).test))));
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 55, 9))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 54, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
if (_t1 === "a") {
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 55, 9))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 54, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above, without the parenthesis
const { type: _t2 } = this.test;
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 61, 9))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 54, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
if (_t2 === "a") {
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 61, 9))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 54, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above without parenthesis at both places
const { type: testType } = this.test;
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 67, 9))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 54, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
if (testType === "a") {
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 67, 9))
this.test.name;
>this.test.name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 54, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 54, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
}
function _tcb4(this: { test: Test }) {
>_tcb4 : Symbol(_tcb4, Decl(controlFlowAliasing2.ts, 71, 1))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 73, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>Test : Symbol(Test, Decl(controlFlowAliasing2.ts, 0, 0))
const { type: _t1 } = (((((this).test))));
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 74, 9))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 73, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
if ("a" === _t1) {
>_t1 : Symbol(_t1, Decl(controlFlowAliasing2.ts, 74, 9))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 73, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above, without the parenthesis
const { type: _t2 } = this.test;
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 80, 9))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 73, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
if ("a" === _t2) {
>_t2 : Symbol(_t2, Decl(controlFlowAliasing2.ts, 80, 9))
(((((this).test)).name));
>(((this).test)).name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>(this).test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 73, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
// Same as above without parenthesis at both places
const { type: testType } = this.test;
>type : Symbol(type, Decl(controlFlowAliasing2.ts, 4, 17), Decl(controlFlowAliasing2.ts, 9, 17))
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 86, 9))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 73, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
if ("a" === testType) {
>testType : Symbol(testType, Decl(controlFlowAliasing2.ts, 86, 9))
this.test.name;
>this.test.name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
>this.test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>this : Symbol(this, Decl(controlFlowAliasing2.ts, 73, 15))
>test : Symbol(test, Decl(controlFlowAliasing2.ts, 73, 22))
>name : Symbol(TestA.name, Decl(controlFlowAliasing2.ts, 5, 12))
}
}
export {};

View File

@ -0,0 +1,605 @@
//// [tests/cases/conformance/controlFlow/controlFlowAliasing2.ts] ////
=== controlFlowAliasing2.ts ===
// https://github.com/microsoft/TypeScript/issues/61784
type Test = TestA | TestB;
>Test : Test
> : ^^^^
interface TestA {
type: 'a';
>type : "a"
> : ^^^
name: string;
>name : string
> : ^^^^^^
}
interface TestB {
type: 'b';
>type : "b"
> : ^^^
value: number;
>value : number
> : ^^^^^^
}
function _tcb1(this: { test: Test }) {
>_tcb1 : (this: { test: Test; }) => void
> : ^ ^^ ^^^^^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
// TS generated by Angular's Type Check block
const _t1 = (((((this).test)).type));
>_t1 : "a" | "b"
> : ^^^^^^^^^
>(((((this).test)).type)) : "a" | "b"
> : ^^^^^^^^^
>((((this).test)).type) : "a" | "b"
> : ^^^^^^^^^
>(((this).test)).type : "a" | "b"
> : ^^^^^^^^^
>(((this).test)) : Test
> : ^^^^
>((this).test) : Test
> : ^^^^
>(this).test : Test
> : ^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
>type : "a" | "b"
> : ^^^^^^^^^
if (_t1 === "a") {
>_t1 === "a" : boolean
> : ^^^^^^^
>_t1 : "a" | "b"
> : ^^^^^^^^^
>"a" : "a"
> : ^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above, without the parenthesis
const _t2 = this.test.type;
>_t2 : "a" | "b"
> : ^^^^^^^^^
>this.test.type : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
>type : "a" | "b"
> : ^^^^^^^^^
if (_t2 === "a") {
>_t2 === "a" : boolean
> : ^^^^^^^
>_t2 : "a" | "b"
> : ^^^^^^^^^
>"a" : "a"
> : ^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above without parenthesis at both places
const testType = this.test.type;
>testType : "a" | "b"
> : ^^^^^^^^^
>this.test.type : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
>type : "a" | "b"
> : ^^^^^^^^^
if (testType === "a") {
>testType === "a" : boolean
> : ^^^^^^^
>testType : "a" | "b"
> : ^^^^^^^^^
>"a" : "a"
> : ^^^
this.test.name;
>this.test.name : string
> : ^^^^^^
>this.test : TestA
> : ^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
}
function _tcb2(this: { test: Test }) {
>_tcb2 : (this: { test: Test; }) => void
> : ^ ^^ ^^^^^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
// TS generated by Angular's Type Check block
const _t1 = (((((this).test)).type));
>_t1 : "a" | "b"
> : ^^^^^^^^^
>(((((this).test)).type)) : "a" | "b"
> : ^^^^^^^^^
>((((this).test)).type) : "a" | "b"
> : ^^^^^^^^^
>(((this).test)).type : "a" | "b"
> : ^^^^^^^^^
>(((this).test)) : Test
> : ^^^^
>((this).test) : Test
> : ^^^^
>(this).test : Test
> : ^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
>type : "a" | "b"
> : ^^^^^^^^^
if ("a" === _t1) {
>"a" === _t1 : boolean
> : ^^^^^^^
>"a" : "a"
> : ^^^
>_t1 : "a" | "b"
> : ^^^^^^^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above, without the parenthesis
const _t2 = this.test.type;
>_t2 : "a" | "b"
> : ^^^^^^^^^
>this.test.type : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
>type : "a" | "b"
> : ^^^^^^^^^
if ("a" === _t2) {
>"a" === _t2 : boolean
> : ^^^^^^^
>"a" : "a"
> : ^^^
>_t2 : "a" | "b"
> : ^^^^^^^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above without parenthesis at both places
const testType = this.test.type;
>testType : "a" | "b"
> : ^^^^^^^^^
>this.test.type : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
>type : "a" | "b"
> : ^^^^^^^^^
if ("a" === testType) {
>"a" === testType : boolean
> : ^^^^^^^
>"a" : "a"
> : ^^^
>testType : "a" | "b"
> : ^^^^^^^^^
this.test.name;
>this.test.name : string
> : ^^^^^^
>this.test : TestA
> : ^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
}
function _tcb3(this: { test: Test }) {
>_tcb3 : (this: { test: Test; }) => void
> : ^ ^^ ^^^^^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
const { type: _t1 } = (((((this).test))));
>type : any
> : ^^^
>_t1 : "a" | "b"
> : ^^^^^^^^^
>(((((this).test)))) : Test
> : ^^^^
>((((this).test))) : Test
> : ^^^^
>(((this).test)) : Test
> : ^^^^
>((this).test) : Test
> : ^^^^
>(this).test : Test
> : ^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
if (_t1 === "a") {
>_t1 === "a" : boolean
> : ^^^^^^^
>_t1 : "a" | "b"
> : ^^^^^^^^^
>"a" : "a"
> : ^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above, without the parenthesis
const { type: _t2 } = this.test;
>type : any
> : ^^^
>_t2 : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
if (_t2 === "a") {
>_t2 === "a" : boolean
> : ^^^^^^^
>_t2 : "a" | "b"
> : ^^^^^^^^^
>"a" : "a"
> : ^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above without parenthesis at both places
const { type: testType } = this.test;
>type : any
> : ^^^
>testType : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
if (testType === "a") {
>testType === "a" : boolean
> : ^^^^^^^
>testType : "a" | "b"
> : ^^^^^^^^^
>"a" : "a"
> : ^^^
this.test.name;
>this.test.name : string
> : ^^^^^^
>this.test : TestA
> : ^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
}
function _tcb4(this: { test: Test }) {
>_tcb4 : (this: { test: Test; }) => void
> : ^ ^^ ^^^^^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
const { type: _t1 } = (((((this).test))));
>type : any
> : ^^^
>_t1 : "a" | "b"
> : ^^^^^^^^^
>(((((this).test)))) : Test
> : ^^^^
>((((this).test))) : Test
> : ^^^^
>(((this).test)) : Test
> : ^^^^
>((this).test) : Test
> : ^^^^
>(this).test : Test
> : ^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
if ("a" === _t1) {
>"a" === _t1 : boolean
> : ^^^^^^^
>"a" : "a"
> : ^^^
>_t1 : "a" | "b"
> : ^^^^^^^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above, without the parenthesis
const { type: _t2 } = this.test;
>type : any
> : ^^^
>_t2 : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
if ("a" === _t2) {
>"a" === _t2 : boolean
> : ^^^^^^^
>"a" : "a"
> : ^^^
>_t2 : "a" | "b"
> : ^^^^^^^^^
(((((this).test)).name));
>(((((this).test)).name)) : string
> : ^^^^^^
>((((this).test)).name) : string
> : ^^^^^^
>(((this).test)).name : string
> : ^^^^^^
>(((this).test)) : TestA
> : ^^^^^
>((this).test) : TestA
> : ^^^^^
>(this).test : TestA
> : ^^^^^
>(this) : { test: Test; }
> : ^^^^^^^^ ^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
// Same as above without parenthesis at both places
const { type: testType } = this.test;
>type : any
> : ^^^
>testType : "a" | "b"
> : ^^^^^^^^^
>this.test : Test
> : ^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : Test
> : ^^^^
if ("a" === testType) {
>"a" === testType : boolean
> : ^^^^^^^
>"a" : "a"
> : ^^^
>testType : "a" | "b"
> : ^^^^^^^^^
this.test.name;
>this.test.name : string
> : ^^^^^^
>this.test : TestA
> : ^^^^^
>this : { test: Test; }
> : ^^^^^^^^ ^^^
>test : TestA
> : ^^^^^
>name : string
> : ^^^^^^
}
}
export {};

View File

@ -0,0 +1,96 @@
// @strict: true
// @noEmit: true
// https://github.com/microsoft/TypeScript/issues/61784
type Test = TestA | TestB;
interface TestA {
type: 'a';
name: string;
}
interface TestB {
type: 'b';
value: number;
}
function _tcb1(this: { test: Test }) {
// TS generated by Angular's Type Check block
const _t1 = (((((this).test)).type));
if (_t1 === "a") {
(((((this).test)).name));
}
// Same as above, without the parenthesis
const _t2 = this.test.type;
if (_t2 === "a") {
(((((this).test)).name));
}
// Same as above without parenthesis at both places
const testType = this.test.type;
if (testType === "a") {
this.test.name;
}
}
function _tcb2(this: { test: Test }) {
// TS generated by Angular's Type Check block
const _t1 = (((((this).test)).type));
if ("a" === _t1) {
(((((this).test)).name));
}
// Same as above, without the parenthesis
const _t2 = this.test.type;
if ("a" === _t2) {
(((((this).test)).name));
}
// Same as above without parenthesis at both places
const testType = this.test.type;
if ("a" === testType) {
this.test.name;
}
}
function _tcb3(this: { test: Test }) {
const { type: _t1 } = (((((this).test))));
if (_t1 === "a") {
(((((this).test)).name));
}
// Same as above, without the parenthesis
const { type: _t2 } = this.test;
if (_t2 === "a") {
(((((this).test)).name));
}
// Same as above without parenthesis at both places
const { type: testType } = this.test;
if (testType === "a") {
this.test.name;
}
}
function _tcb4(this: { test: Test }) {
const { type: _t1 } = (((((this).test))));
if ("a" === _t1) {
(((((this).test)).name));
}
// Same as above, without the parenthesis
const { type: _t2 } = this.test;
if ("a" === _t2) {
(((((this).test)).name));
}
// Same as above without parenthesis at both places
const { type: testType } = this.test;
if ("a" === testType) {
this.test.name;
}
}
export {};