diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 54e7c3cdbd4..081f99f01f8 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3238,7 +3238,7 @@ namespace ts { // If this is a property-parameter, then also declare the property symbol into the // containing class. if (isParameterPropertyDeclaration(node, node.parent)) { - const classDeclaration = node.parent.parent; + const classDeclaration = node.parent.parent; declareSymbol(classDeclaration.symbol.members!, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 09059bc070f..d3a50070d3c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18903,10 +18903,6 @@ namespace ts { return false; } - function isSyntheticThisPropertyAccess(expr: Node) { - return isAccessExpression(expr) && expr.expression.kind === SyntaxKind.ThisKeyword && !!(expr.expression.flags & NodeFlags.Synthesized); - } - function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined { let result: Symbol[] | undefined; for (const sourceProperty of sourceProperties) { @@ -19634,7 +19630,7 @@ namespace ts { // on empty arrays are possible without implicit any errors and new element types can be inferred without // type mismatch errors. const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? autoArrayType : finalizeEvolvingArrayType(evolvedType); - if (resultType === unreachableNeverType|| reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { + if (resultType === unreachableNeverType || reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { return declaredType; } return resultType; @@ -20241,11 +20237,6 @@ namespace ts { if (strictNullChecks && optionalChainContainsReference(target, reference) && assumeTrue === (literal.text !== "undefined")) { return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } - // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the - // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, target)) { - return declaredType; - } return type; } if (type.flags & TypeFlags.Any && literal.text === "function") { @@ -20427,16 +20418,6 @@ namespace ts { if (assumeTrue && strictNullChecks && optionalChainContainsReference(left, reference)) { return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } - // For a reference of the form 'x.y', an 'x instanceof T' type guard resets the - // narrowed type of 'y' to its declared type. We do this because preceding 'x.y' - // references might reference a different 'y' property. However, we make an exception - // for property accesses where x is a synthetic 'this' expression, indicating that we - // were called from isPropertyInitializedInConstructor. Without this exception, - // initializations of 'this' properties that occur before a 'this instanceof XXX' - // check would not be considered. - if (containsMatchingReference(reference, left) && !isSyntheticThisPropertyAccess(reference)) { - return declaredType; - } return type; } @@ -20517,9 +20498,6 @@ namespace ts { !(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) { return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } - if (containsMatchingReference(reference, predicateArgument)) { - return declaredType; - } } } return type; diff --git a/tests/baselines/reference/narrowingOrderIndependent.js b/tests/baselines/reference/narrowingOrderIndependent.js new file mode 100644 index 00000000000..c8c965a6e0f --- /dev/null +++ b/tests/baselines/reference/narrowingOrderIndependent.js @@ -0,0 +1,66 @@ +//// [narrowingOrderIndependent.ts] +// Repro from #36709 + +class A { + constructor(public stringOrUndefined: string | undefined) {} +} + +class B { + constructor(public str: string) {} +} + +const a = new A("123"); + +if (a instanceof A && a.stringOrUndefined) { + new B(a.stringOrUndefined) +} + +if (a.stringOrUndefined && a instanceof A) { + new B(a.stringOrUndefined) +} + +if (a instanceof A) { + if (a.stringOrUndefined) { + new B(a.stringOrUndefined) + } +} + +if (a.stringOrUndefined) { + if (a instanceof A) { + new B(a.stringOrUndefined) + } +} + + +//// [narrowingOrderIndependent.js] +"use strict"; +// Repro from #36709 +var A = /** @class */ (function () { + function A(stringOrUndefined) { + this.stringOrUndefined = stringOrUndefined; + } + return A; +}()); +var B = /** @class */ (function () { + function B(str) { + this.str = str; + } + return B; +}()); +var a = new A("123"); +if (a instanceof A && a.stringOrUndefined) { + new B(a.stringOrUndefined); +} +if (a.stringOrUndefined && a instanceof A) { + new B(a.stringOrUndefined); +} +if (a instanceof A) { + if (a.stringOrUndefined) { + new B(a.stringOrUndefined); + } +} +if (a.stringOrUndefined) { + if (a instanceof A) { + new B(a.stringOrUndefined); + } +} diff --git a/tests/baselines/reference/narrowingOrderIndependent.symbols b/tests/baselines/reference/narrowingOrderIndependent.symbols new file mode 100644 index 00000000000..bbebef545dd --- /dev/null +++ b/tests/baselines/reference/narrowingOrderIndependent.symbols @@ -0,0 +1,83 @@ +=== tests/cases/compiler/narrowingOrderIndependent.ts === +// Repro from #36709 + +class A { +>A : Symbol(A, Decl(narrowingOrderIndependent.ts, 0, 0)) + + constructor(public stringOrUndefined: string | undefined) {} +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +} + +class B { +>B : Symbol(B, Decl(narrowingOrderIndependent.ts, 4, 1)) + + constructor(public str: string) {} +>str : Symbol(B.str, Decl(narrowingOrderIndependent.ts, 7, 16)) +} + +const a = new A("123"); +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>A : Symbol(A, Decl(narrowingOrderIndependent.ts, 0, 0)) + +if (a instanceof A && a.stringOrUndefined) { +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>A : Symbol(A, Decl(narrowingOrderIndependent.ts, 0, 0)) +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) + + new B(a.stringOrUndefined) +>B : Symbol(B, Decl(narrowingOrderIndependent.ts, 4, 1)) +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +} + +if (a.stringOrUndefined && a instanceof A) { +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>A : Symbol(A, Decl(narrowingOrderIndependent.ts, 0, 0)) + + new B(a.stringOrUndefined) +>B : Symbol(B, Decl(narrowingOrderIndependent.ts, 4, 1)) +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +} + +if (a instanceof A) { +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>A : Symbol(A, Decl(narrowingOrderIndependent.ts, 0, 0)) + + if (a.stringOrUndefined) { +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) + + new B(a.stringOrUndefined) +>B : Symbol(B, Decl(narrowingOrderIndependent.ts, 4, 1)) +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) + } +} + +if (a.stringOrUndefined) { +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) + + if (a instanceof A) { +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>A : Symbol(A, Decl(narrowingOrderIndependent.ts, 0, 0)) + + new B(a.stringOrUndefined) +>B : Symbol(B, Decl(narrowingOrderIndependent.ts, 4, 1)) +>a.stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) +>a : Symbol(a, Decl(narrowingOrderIndependent.ts, 10, 5)) +>stringOrUndefined : Symbol(A.stringOrUndefined, Decl(narrowingOrderIndependent.ts, 3, 16)) + } +} + diff --git a/tests/baselines/reference/narrowingOrderIndependent.types b/tests/baselines/reference/narrowingOrderIndependent.types new file mode 100644 index 00000000000..e777c794171 --- /dev/null +++ b/tests/baselines/reference/narrowingOrderIndependent.types @@ -0,0 +1,95 @@ +=== tests/cases/compiler/narrowingOrderIndependent.ts === +// Repro from #36709 + +class A { +>A : A + + constructor(public stringOrUndefined: string | undefined) {} +>stringOrUndefined : string | undefined +} + +class B { +>B : B + + constructor(public str: string) {} +>str : string +} + +const a = new A("123"); +>a : A +>new A("123") : A +>A : typeof A +>"123" : "123" + +if (a instanceof A && a.stringOrUndefined) { +>a instanceof A && a.stringOrUndefined : string | false | undefined +>a instanceof A : boolean +>a : A +>A : typeof A +>a.stringOrUndefined : string | undefined +>a : A +>stringOrUndefined : string | undefined + + new B(a.stringOrUndefined) +>new B(a.stringOrUndefined) : B +>B : typeof B +>a.stringOrUndefined : string +>a : A +>stringOrUndefined : string +} + +if (a.stringOrUndefined && a instanceof A) { +>a.stringOrUndefined && a instanceof A : boolean | "" | undefined +>a.stringOrUndefined : string | undefined +>a : A +>stringOrUndefined : string | undefined +>a instanceof A : boolean +>a : A +>A : typeof A + + new B(a.stringOrUndefined) +>new B(a.stringOrUndefined) : B +>B : typeof B +>a.stringOrUndefined : string +>a : A +>stringOrUndefined : string +} + +if (a instanceof A) { +>a instanceof A : boolean +>a : A +>A : typeof A + + if (a.stringOrUndefined) { +>a.stringOrUndefined : string | undefined +>a : A +>stringOrUndefined : string | undefined + + new B(a.stringOrUndefined) +>new B(a.stringOrUndefined) : B +>B : typeof B +>a.stringOrUndefined : string +>a : A +>stringOrUndefined : string + } +} + +if (a.stringOrUndefined) { +>a.stringOrUndefined : string | undefined +>a : A +>stringOrUndefined : string | undefined + + if (a instanceof A) { +>a instanceof A : boolean +>a : A +>A : typeof A + + new B(a.stringOrUndefined) +>new B(a.stringOrUndefined) : B +>B : typeof B +>a.stringOrUndefined : string +>a : A +>stringOrUndefined : string + } +} + diff --git a/tests/cases/compiler/narrowingOrderIndependent.ts b/tests/cases/compiler/narrowingOrderIndependent.ts new file mode 100644 index 00000000000..9d8a362e75b --- /dev/null +++ b/tests/cases/compiler/narrowingOrderIndependent.ts @@ -0,0 +1,33 @@ +// @strict: true + +// Repro from #36709 + +class A { + constructor(public stringOrUndefined: string | undefined) {} +} + +class B { + constructor(public str: string) {} +} + +const a = new A("123"); + +if (a instanceof A && a.stringOrUndefined) { + new B(a.stringOrUndefined) +} + +if (a.stringOrUndefined && a instanceof A) { + new B(a.stringOrUndefined) +} + +if (a instanceof A) { + if (a.stringOrUndefined) { + new B(a.stringOrUndefined) + } +} + +if (a.stringOrUndefined) { + if (a instanceof A) { + new B(a.stringOrUndefined) + } +}