diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e256b43e3b495..4a896d2a70fc2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20147,6 +20147,12 @@ namespace ts { if (right.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(left)) { return narrowTypeByTypeof(type, right, operator, left, assumeTrue); } + if (isConstructorAccessExpression(left)) { + return narrowTypeByConstructor(type, left, operator, right, assumeTrue); + } + if (isConstructorAccessExpression(right)) { + return narrowTypeByConstructor(type, right, operator, left, assumeTrue); + } if (isMatchingReference(reference, left)) { return narrowTypeByEquality(type, operator, right, assumeTrue); } @@ -20432,6 +20438,59 @@ namespace ts { return getTypeWithFacts(mapType(type, narrowTypeForTypeofSwitch(impliedType)), switchFacts); } + function narrowTypeByConstructor(type: Type, constructorAccessExpr: AccessExpression, operator: SyntaxKind, identifier: Expression, assumeTrue: boolean): Type { + // Do not narrow when checking inequality. + if (assumeTrue ? (operator !== SyntaxKind.EqualsEqualsToken && operator !== SyntaxKind.EqualsEqualsEqualsToken) : (operator !== SyntaxKind.ExclamationEqualsToken && operator !== SyntaxKind.ExclamationEqualsEqualsToken)) { + return type; + } + + // In the case of `x.y`, a `x.constructor === T` type guard resets the narrowed type of `y` to its declared type. + if (!isMatchingReference(reference, constructorAccessExpr.expression)) { + return declaredType; + } + + // Get the type of the constructor identifier expression, if it is not a function then do not narrow. + const identifierType = getTypeOfExpression(identifier); + if (!isFunctionType(identifierType) && !isConstructorType(identifierType)) { + return type; + } + + // Get the prototype property of the type identifier so we can find out its type. + const prototypeProperty = getPropertyOfType(identifierType, "prototype" as __String); + if (!prototypeProperty) { + return type; + } + + // Get the type of the prototype, if it is undefined, or the global `Object` or `Function` types then do not narrow. + const prototypeType = getTypeOfSymbol(prototypeProperty); + const candidate = !isTypeAny(prototypeType) ? prototypeType : undefined; + if (!candidate || candidate === globalObjectType || candidate === globalFunctionType) { + return type; + } + + // If the type that is being narrowed is `any` then just return the `candidate` type since every type is a subtype of `any`. + if (isTypeAny(type)) { + return candidate; + } + + // Filter out types that are not considered to be "constructed by" the `candidate` type. + return filterType(type, t => isConstructedBy(t, candidate)); + + function isConstructedBy(source: Type, target: Type) { + // If either the source or target type are a class type then we need to check that they are the same exact type. + // This is because you may have a class `A` that defines some set of properties, and another class `B` + // that defines the same set of properties as class `A`, in that case they are structurally the same + // type, but when you do something like `instanceOfA.constructor === B` it will return false. + if (source.flags & TypeFlags.Object && getObjectFlags(source) & ObjectFlags.Class || + target.flags & TypeFlags.Object && getObjectFlags(target) & ObjectFlags.Class) { + return source.symbol === target.symbol; + } + + // For all other types just check that the `source` type is a subtype of the `target` type. + return isTypeSubtypeOf(source, target); + } + } + function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { const left = getReferenceCandidate(expr.left); if (!isMatchingReference(reference, left)) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 087e047032e5b..65c2b4d4407bd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4426,6 +4426,13 @@ namespace ts { return isPropertyAccessExpression(node) && isEntityNameExpression(node.expression); } + export function isConstructorAccessExpression(expr: Expression): expr is AccessExpression { + return ( + isPropertyAccessExpression(expr) && idText(expr.name) === "constructor" || + isElementAccessExpression(expr) && isStringLiteralLike(expr.argumentExpression) && expr.argumentExpression.text === "constructor" + ); + } + export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): string | undefined { if (isPropertyAccessExpression(expr)) { const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression); diff --git a/tests/baselines/reference/typeGuardConstructorClassAndNumber.errors.txt b/tests/baselines/reference/typeGuardConstructorClassAndNumber.errors.txt new file mode 100644 index 0000000000000..b356fddd14ef4 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorClassAndNumber.errors.txt @@ -0,0 +1,163 @@ +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(66,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(73,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(80,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(87,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(94,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(101,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(108,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. +tests/cases/compiler/typeGuardConstructorClassAndNumber.ts(115,10): error TS2339: Property 'property1' does not exist on type 'number | C1'. + Property 'property1' does not exist on type 'number'. + + +==== tests/cases/compiler/typeGuardConstructorClassAndNumber.ts (8 errors) ==== + // Typical case + class C1 { + property1: string; + } + + let var1: C1 | number; + if (var1.constructor == C1) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + if (var1["constructor"] == C1) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + if (var1.constructor === C1) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + if (var1["constructor"] === C1) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + if (C1 == var1.constructor) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + if (C1 == var1["constructor"]) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + if (C1 === var1.constructor) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + if (C1 === var1["constructor"]) { + var1; // C1 + var1.property1; // string + } + else { + var1; // number | C1 + } + + if (var1.constructor != C1) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + if (var1["constructor"] != C1) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + if (var1.constructor !== C1) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + if (var1["constructor"] !== C1) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + if (C1 != var1.constructor) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + if (C1 != var1["constructor"]) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + if (C1 !== var1.constructor) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + if (C1 !== var1["constructor"]) { + var1; // C1 | number + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'number | C1'. +!!! error TS2339: Property 'property1' does not exist on type 'number'. + } + else { + var1; // C1 + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardConstructorClassAndNumber.js b/tests/baselines/reference/typeGuardConstructorClassAndNumber.js new file mode 100644 index 0000000000000..188de4cf06733 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorClassAndNumber.js @@ -0,0 +1,242 @@ +//// [typeGuardConstructorClassAndNumber.ts] +// Typical case +class C1 { + property1: string; +} + +let var1: C1 | number; +if (var1.constructor == C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1["constructor"] == C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1.constructor === C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1["constructor"] === C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 == var1.constructor) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 == var1["constructor"]) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 === var1.constructor) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 === var1["constructor"]) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} + +if (var1.constructor != C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1["constructor"] != C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1.constructor !== C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1["constructor"] !== C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 != var1.constructor) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 != var1["constructor"]) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 !== var1.constructor) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 !== var1["constructor"]) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} + + +//// [typeGuardConstructorClassAndNumber.js] +// Typical case +var C1 = /** @class */ (function () { + function C1() { + } + return C1; +}()); +var var1; +if (var1.constructor == C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1["constructor"] == C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1.constructor === C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1["constructor"] === C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 == var1.constructor) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 == var1["constructor"]) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 === var1.constructor) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 === var1["constructor"]) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1.constructor != C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1["constructor"] != C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1.constructor !== C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1["constructor"] !== C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 != var1.constructor) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 != var1["constructor"]) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 !== var1.constructor) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 !== var1["constructor"]) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} diff --git a/tests/baselines/reference/typeGuardConstructorClassAndNumber.symbols b/tests/baselines/reference/typeGuardConstructorClassAndNumber.symbols new file mode 100644 index 0000000000000..893ad03fad252 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorClassAndNumber.symbols @@ -0,0 +1,279 @@ +=== tests/cases/compiler/typeGuardConstructorClassAndNumber.ts === +// Typical case +class C1 { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + property1: string; +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} + +let var1: C1 | number; +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + +if (var1.constructor == C1) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (var1["constructor"] == C1) { +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (var1.constructor === C1) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (var1["constructor"] === C1) { +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 == var1.constructor) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 == var1["constructor"]) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 === var1.constructor) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 === var1["constructor"]) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // string +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorClassAndNumber.ts, 1, 10)) +} +else { + var1; // number | C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} + +if (var1.constructor != C1) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (var1["constructor"] != C1) { +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (var1.constructor !== C1) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (var1["constructor"] !== C1) { +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 != var1.constructor) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 != var1["constructor"]) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 !== var1.constructor) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +if (C1 !== var1["constructor"]) { +>C1 : Symbol(C1, Decl(typeGuardConstructorClassAndNumber.ts, 0, 0)) +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) + + var1; // C1 | number +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} +else { + var1; // C1 +>var1 : Symbol(var1, Decl(typeGuardConstructorClassAndNumber.ts, 5, 3)) +} + diff --git a/tests/baselines/reference/typeGuardConstructorClassAndNumber.types b/tests/baselines/reference/typeGuardConstructorClassAndNumber.types new file mode 100644 index 0000000000000..1c4c2fb523091 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorClassAndNumber.types @@ -0,0 +1,318 @@ +=== tests/cases/compiler/typeGuardConstructorClassAndNumber.ts === +// Typical case +class C1 { +>C1 : C1 + + property1: string; +>property1 : string +} + +let var1: C1 | number; +>var1 : number | C1 + +if (var1.constructor == C1) { +>var1.constructor == C1 : boolean +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function +>C1 : typeof C1 + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} +if (var1["constructor"] == C1) { +>var1["constructor"] == C1 : boolean +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" +>C1 : typeof C1 + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} +if (var1.constructor === C1) { +>var1.constructor === C1 : boolean +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function +>C1 : typeof C1 + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} +if (var1["constructor"] === C1) { +>var1["constructor"] === C1 : boolean +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" +>C1 : typeof C1 + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} +if (C1 == var1.constructor) { +>C1 == var1.constructor : boolean +>C1 : typeof C1 +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} +if (C1 == var1["constructor"]) { +>C1 == var1["constructor"] : boolean +>C1 : typeof C1 +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} +if (C1 === var1.constructor) { +>C1 === var1.constructor : boolean +>C1 : typeof C1 +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} +if (C1 === var1["constructor"]) { +>C1 === var1["constructor"] : boolean +>C1 : typeof C1 +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" + + var1; // C1 +>var1 : C1 + + var1.property1; // string +>var1.property1 : string +>var1 : C1 +>property1 : string +} +else { + var1; // number | C1 +>var1 : number | C1 +} + +if (var1.constructor != C1) { +>var1.constructor != C1 : boolean +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function +>C1 : typeof C1 + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} +if (var1["constructor"] != C1) { +>var1["constructor"] != C1 : boolean +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" +>C1 : typeof C1 + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} +if (var1.constructor !== C1) { +>var1.constructor !== C1 : boolean +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function +>C1 : typeof C1 + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} +if (var1["constructor"] !== C1) { +>var1["constructor"] !== C1 : boolean +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" +>C1 : typeof C1 + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} +if (C1 != var1.constructor) { +>C1 != var1.constructor : boolean +>C1 : typeof C1 +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} +if (C1 != var1["constructor"]) { +>C1 != var1["constructor"] : boolean +>C1 : typeof C1 +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} +if (C1 !== var1.constructor) { +>C1 !== var1.constructor : boolean +>C1 : typeof C1 +>var1.constructor : Function +>var1 : number | C1 +>constructor : Function + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} +if (C1 !== var1["constructor"]) { +>C1 !== var1["constructor"] : boolean +>C1 : typeof C1 +>var1["constructor"] : Function +>var1 : number | C1 +>"constructor" : "constructor" + + var1; // C1 | number +>var1 : number | C1 + + var1.property1; // error +>var1.property1 : any +>var1 : number | C1 +>property1 : any +} +else { + var1; // C1 +>var1 : C1 +} + diff --git a/tests/baselines/reference/typeGuardConstructorDerivedClass.errors.txt b/tests/baselines/reference/typeGuardConstructorDerivedClass.errors.txt new file mode 100644 index 0000000000000..c0c9e3d88a402 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorDerivedClass.errors.txt @@ -0,0 +1,72 @@ +tests/cases/compiler/typeGuardConstructorDerivedClass.ts(13,10): error TS2339: Property 'property1' does not exist on type 'never'. + + +==== tests/cases/compiler/typeGuardConstructorDerivedClass.ts (1 errors) ==== + // Derived class with different structures + class C1 { + property1: number; + } + + class C2 extends C1 { + property2: number; + } + + let var1: C2 | string; + if (var1.constructor === C1) { + var1; // never + var1.property1; // error + ~~~~~~~~~ +!!! error TS2339: Property 'property1' does not exist on type 'never'. + } + if (var1.constructor === C2) { + var1; // C2 + var1.property1; // number + } + + // Derived classes with the same structure + class C3 {} + + class C4 extends C3 {} + + let var2: C4 | string; + if (var2.constructor === C3) { + var2; // never + } + if (var2.constructor === C4) { + var2; // C4 + } + + // Disjointly structured classes + class C5 { + property1: number; + } + + class C6 { + property2: number; + } + + let let3: C6 | string; + if (let3.constructor === C5) { + let3; // never + } + if (let3.constructor === C6) { + let3; // C6 + } + + // Classes with the same structure + class C7 { + property1: number + } + + class C8 { + property1: number; + } + + let let4: C8 | string; + if (let4.constructor === C7) { + let4; // never + } + if (let4.constructor === C8) { + let4; // C8 + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardConstructorDerivedClass.js b/tests/baselines/reference/typeGuardConstructorDerivedClass.js new file mode 100644 index 0000000000000..8ffabcb987a90 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorDerivedClass.js @@ -0,0 +1,160 @@ +//// [typeGuardConstructorDerivedClass.ts] +// Derived class with different structures +class C1 { + property1: number; +} + +class C2 extends C1 { + property2: number; +} + +let var1: C2 | string; +if (var1.constructor === C1) { + var1; // never + var1.property1; // error +} +if (var1.constructor === C2) { + var1; // C2 + var1.property1; // number +} + +// Derived classes with the same structure +class C3 {} + +class C4 extends C3 {} + +let var2: C4 | string; +if (var2.constructor === C3) { + var2; // never +} +if (var2.constructor === C4) { + var2; // C4 +} + +// Disjointly structured classes +class C5 { + property1: number; +} + +class C6 { + property2: number; +} + +let let3: C6 | string; +if (let3.constructor === C5) { + let3; // never +} +if (let3.constructor === C6) { + let3; // C6 +} + +// Classes with the same structure +class C7 { + property1: number +} + +class C8 { + property1: number; +} + +let let4: C8 | string; +if (let4.constructor === C7) { + let4; // never +} +if (let4.constructor === C8) { + let4; // C8 +} + + +//// [typeGuardConstructorDerivedClass.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +// Derived class with different structures +var C1 = /** @class */ (function () { + function C1() { + } + return C1; +}()); +var C2 = /** @class */ (function (_super) { + __extends(C2, _super); + function C2() { + return _super !== null && _super.apply(this, arguments) || this; + } + return C2; +}(C1)); +var var1; +if (var1.constructor === C1) { + var1; // never + var1.property1; // error +} +if (var1.constructor === C2) { + var1; // C2 + var1.property1; // number +} +// Derived classes with the same structure +var C3 = /** @class */ (function () { + function C3() { + } + return C3; +}()); +var C4 = /** @class */ (function (_super) { + __extends(C4, _super); + function C4() { + return _super !== null && _super.apply(this, arguments) || this; + } + return C4; +}(C3)); +var var2; +if (var2.constructor === C3) { + var2; // never +} +if (var2.constructor === C4) { + var2; // C4 +} +// Disjointly structured classes +var C5 = /** @class */ (function () { + function C5() { + } + return C5; +}()); +var C6 = /** @class */ (function () { + function C6() { + } + return C6; +}()); +var let3; +if (let3.constructor === C5) { + let3; // never +} +if (let3.constructor === C6) { + let3; // C6 +} +// Classes with the same structure +var C7 = /** @class */ (function () { + function C7() { + } + return C7; +}()); +var C8 = /** @class */ (function () { + function C8() { + } + return C8; +}()); +var let4; +if (let4.constructor === C7) { + let4; // never +} +if (let4.constructor === C8) { + let4; // C8 +} diff --git a/tests/baselines/reference/typeGuardConstructorDerivedClass.symbols b/tests/baselines/reference/typeGuardConstructorDerivedClass.symbols new file mode 100644 index 0000000000000..acb2e8890a2f9 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorDerivedClass.symbols @@ -0,0 +1,155 @@ +=== tests/cases/compiler/typeGuardConstructorDerivedClass.ts === +// Derived class with different structures +class C1 { +>C1 : Symbol(C1, Decl(typeGuardConstructorDerivedClass.ts, 0, 0)) + + property1: number; +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorDerivedClass.ts, 1, 10)) +} + +class C2 extends C1 { +>C2 : Symbol(C2, Decl(typeGuardConstructorDerivedClass.ts, 3, 1)) +>C1 : Symbol(C1, Decl(typeGuardConstructorDerivedClass.ts, 0, 0)) + + property2: number; +>property2 : Symbol(C2.property2, Decl(typeGuardConstructorDerivedClass.ts, 5, 21)) +} + +let var1: C2 | string; +>var1 : Symbol(var1, Decl(typeGuardConstructorDerivedClass.ts, 9, 3)) +>C2 : Symbol(C2, Decl(typeGuardConstructorDerivedClass.ts, 3, 1)) + +if (var1.constructor === C1) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorDerivedClass.ts, 9, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructorDerivedClass.ts, 0, 0)) + + var1; // never +>var1 : Symbol(var1, Decl(typeGuardConstructorDerivedClass.ts, 9, 3)) + + var1.property1; // error +>var1 : Symbol(var1, Decl(typeGuardConstructorDerivedClass.ts, 9, 3)) +} +if (var1.constructor === C2) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorDerivedClass.ts, 9, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C2 : Symbol(C2, Decl(typeGuardConstructorDerivedClass.ts, 3, 1)) + + var1; // C2 +>var1 : Symbol(var1, Decl(typeGuardConstructorDerivedClass.ts, 9, 3)) + + var1.property1; // number +>var1.property1 : Symbol(C1.property1, Decl(typeGuardConstructorDerivedClass.ts, 1, 10)) +>var1 : Symbol(var1, Decl(typeGuardConstructorDerivedClass.ts, 9, 3)) +>property1 : Symbol(C1.property1, Decl(typeGuardConstructorDerivedClass.ts, 1, 10)) +} + +// Derived classes with the same structure +class C3 {} +>C3 : Symbol(C3, Decl(typeGuardConstructorDerivedClass.ts, 17, 1)) + +class C4 extends C3 {} +>C4 : Symbol(C4, Decl(typeGuardConstructorDerivedClass.ts, 20, 11)) +>C3 : Symbol(C3, Decl(typeGuardConstructorDerivedClass.ts, 17, 1)) + +let var2: C4 | string; +>var2 : Symbol(var2, Decl(typeGuardConstructorDerivedClass.ts, 24, 3)) +>C4 : Symbol(C4, Decl(typeGuardConstructorDerivedClass.ts, 20, 11)) + +if (var2.constructor === C3) { +>var2.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var2 : Symbol(var2, Decl(typeGuardConstructorDerivedClass.ts, 24, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C3 : Symbol(C3, Decl(typeGuardConstructorDerivedClass.ts, 17, 1)) + + var2; // never +>var2 : Symbol(var2, Decl(typeGuardConstructorDerivedClass.ts, 24, 3)) +} +if (var2.constructor === C4) { +>var2.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var2 : Symbol(var2, Decl(typeGuardConstructorDerivedClass.ts, 24, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C4 : Symbol(C4, Decl(typeGuardConstructorDerivedClass.ts, 20, 11)) + + var2; // C4 +>var2 : Symbol(var2, Decl(typeGuardConstructorDerivedClass.ts, 24, 3)) +} + +// Disjointly structured classes +class C5 { +>C5 : Symbol(C5, Decl(typeGuardConstructorDerivedClass.ts, 30, 1)) + + property1: number; +>property1 : Symbol(C5.property1, Decl(typeGuardConstructorDerivedClass.ts, 33, 10)) +} + +class C6 { +>C6 : Symbol(C6, Decl(typeGuardConstructorDerivedClass.ts, 35, 1)) + + property2: number; +>property2 : Symbol(C6.property2, Decl(typeGuardConstructorDerivedClass.ts, 37, 10)) +} + +let let3: C6 | string; +>let3 : Symbol(let3, Decl(typeGuardConstructorDerivedClass.ts, 41, 3)) +>C6 : Symbol(C6, Decl(typeGuardConstructorDerivedClass.ts, 35, 1)) + +if (let3.constructor === C5) { +>let3.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>let3 : Symbol(let3, Decl(typeGuardConstructorDerivedClass.ts, 41, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C5 : Symbol(C5, Decl(typeGuardConstructorDerivedClass.ts, 30, 1)) + + let3; // never +>let3 : Symbol(let3, Decl(typeGuardConstructorDerivedClass.ts, 41, 3)) +} +if (let3.constructor === C6) { +>let3.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>let3 : Symbol(let3, Decl(typeGuardConstructorDerivedClass.ts, 41, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C6 : Symbol(C6, Decl(typeGuardConstructorDerivedClass.ts, 35, 1)) + + let3; // C6 +>let3 : Symbol(let3, Decl(typeGuardConstructorDerivedClass.ts, 41, 3)) +} + +// Classes with the same structure +class C7 { +>C7 : Symbol(C7, Decl(typeGuardConstructorDerivedClass.ts, 47, 1)) + + property1: number +>property1 : Symbol(C7.property1, Decl(typeGuardConstructorDerivedClass.ts, 50, 10)) +} + +class C8 { +>C8 : Symbol(C8, Decl(typeGuardConstructorDerivedClass.ts, 52, 1)) + + property1: number; +>property1 : Symbol(C8.property1, Decl(typeGuardConstructorDerivedClass.ts, 54, 10)) +} + +let let4: C8 | string; +>let4 : Symbol(let4, Decl(typeGuardConstructorDerivedClass.ts, 58, 3)) +>C8 : Symbol(C8, Decl(typeGuardConstructorDerivedClass.ts, 52, 1)) + +if (let4.constructor === C7) { +>let4.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>let4 : Symbol(let4, Decl(typeGuardConstructorDerivedClass.ts, 58, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C7 : Symbol(C7, Decl(typeGuardConstructorDerivedClass.ts, 47, 1)) + + let4; // never +>let4 : Symbol(let4, Decl(typeGuardConstructorDerivedClass.ts, 58, 3)) +} +if (let4.constructor === C8) { +>let4.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>let4 : Symbol(let4, Decl(typeGuardConstructorDerivedClass.ts, 58, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>C8 : Symbol(C8, Decl(typeGuardConstructorDerivedClass.ts, 52, 1)) + + let4; // C8 +>let4 : Symbol(let4, Decl(typeGuardConstructorDerivedClass.ts, 58, 3)) +} + diff --git a/tests/baselines/reference/typeGuardConstructorDerivedClass.types b/tests/baselines/reference/typeGuardConstructorDerivedClass.types new file mode 100644 index 0000000000000..3dc83b26e7dfe --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorDerivedClass.types @@ -0,0 +1,161 @@ +=== tests/cases/compiler/typeGuardConstructorDerivedClass.ts === +// Derived class with different structures +class C1 { +>C1 : C1 + + property1: number; +>property1 : number +} + +class C2 extends C1 { +>C2 : C2 +>C1 : C1 + + property2: number; +>property2 : number +} + +let var1: C2 | string; +>var1 : string | C2 + +if (var1.constructor === C1) { +>var1.constructor === C1 : boolean +>var1.constructor : Function +>var1 : string | C2 +>constructor : Function +>C1 : typeof C1 + + var1; // never +>var1 : never + + var1.property1; // error +>var1.property1 : any +>var1 : never +>property1 : any +} +if (var1.constructor === C2) { +>var1.constructor === C2 : boolean +>var1.constructor : Function +>var1 : string | C2 +>constructor : Function +>C2 : typeof C2 + + var1; // C2 +>var1 : C2 + + var1.property1; // number +>var1.property1 : number +>var1 : C2 +>property1 : number +} + +// Derived classes with the same structure +class C3 {} +>C3 : C3 + +class C4 extends C3 {} +>C4 : C4 +>C3 : C3 + +let var2: C4 | string; +>var2 : string | C4 + +if (var2.constructor === C3) { +>var2.constructor === C3 : boolean +>var2.constructor : Function +>var2 : string | C4 +>constructor : Function +>C3 : typeof C3 + + var2; // never +>var2 : never +} +if (var2.constructor === C4) { +>var2.constructor === C4 : boolean +>var2.constructor : Function +>var2 : string | C4 +>constructor : Function +>C4 : typeof C4 + + var2; // C4 +>var2 : C4 +} + +// Disjointly structured classes +class C5 { +>C5 : C5 + + property1: number; +>property1 : number +} + +class C6 { +>C6 : C6 + + property2: number; +>property2 : number +} + +let let3: C6 | string; +>let3 : string | C6 + +if (let3.constructor === C5) { +>let3.constructor === C5 : boolean +>let3.constructor : Function +>let3 : string | C6 +>constructor : Function +>C5 : typeof C5 + + let3; // never +>let3 : never +} +if (let3.constructor === C6) { +>let3.constructor === C6 : boolean +>let3.constructor : Function +>let3 : string | C6 +>constructor : Function +>C6 : typeof C6 + + let3; // C6 +>let3 : C6 +} + +// Classes with the same structure +class C7 { +>C7 : C7 + + property1: number +>property1 : number +} + +class C8 { +>C8 : C8 + + property1: number; +>property1 : number +} + +let let4: C8 | string; +>let4 : string | C8 + +if (let4.constructor === C7) { +>let4.constructor === C7 : boolean +>let4.constructor : Function +>let4 : string | C8 +>constructor : Function +>C7 : typeof C7 + + let4; // never +>let4 : never +} +if (let4.constructor === C8) { +>let4.constructor === C8 : boolean +>let4.constructor : Function +>let4 : string | C8 +>constructor : Function +>C8 : typeof C8 + + let4; // C8 +>let4 : C8 +} + diff --git a/tests/baselines/reference/typeGuardConstructorNarrowAny.js b/tests/baselines/reference/typeGuardConstructorNarrowAny.js new file mode 100644 index 0000000000000..8fc189bbb8239 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorNarrowAny.js @@ -0,0 +1,45 @@ +//// [typeGuardConstructorNarrowAny.ts] +// Narrowing any to primitives +let var1: any; + +if (var1.constructor === String) { + var1; // String +} +if (var1.constructor === Number) { + var1; // Number +} +if (var1.constructor === Boolean) { + var1; // Boolean +} +if (var1.constructor === Array) { + var1; // any[] +} +if (var1.constructor === Symbol) { + var1; // Symbol +} +if (var1.constructor === BigInt) { + var1; // BigInt +} + + +//// [typeGuardConstructorNarrowAny.js] +// Narrowing any to primitives +let var1; +if (var1.constructor === String) { + var1; // String +} +if (var1.constructor === Number) { + var1; // Number +} +if (var1.constructor === Boolean) { + var1; // Boolean +} +if (var1.constructor === Array) { + var1; // any[] +} +if (var1.constructor === Symbol) { + var1; // Symbol +} +if (var1.constructor === BigInt) { + var1; // BigInt +} diff --git a/tests/baselines/reference/typeGuardConstructorNarrowAny.symbols b/tests/baselines/reference/typeGuardConstructorNarrowAny.symbols new file mode 100644 index 0000000000000..acbc179aa270d --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorNarrowAny.symbols @@ -0,0 +1,48 @@ +=== tests/cases/compiler/typeGuardConstructorNarrowAny.ts === +// Narrowing any to primitives +let var1: any; +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) + +if (var1.constructor === String) { +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --) ... and 5 more) + + var1; // String +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +} +if (var1.constructor === Number) { +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // Number +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +} +if (var1.constructor === Boolean) { +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // Boolean +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +} +if (var1.constructor === Array) { +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 2 more) + + var1; // any[] +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +} +if (var1.constructor === Symbol) { +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) + + var1; // Symbol +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +} +if (var1.constructor === BigInt) { +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +>BigInt : Symbol(BigInt, Decl(lib.es2020.bigint.d.ts, --, --), Decl(lib.es2020.bigint.d.ts, --, --)) + + var1; // BigInt +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowAny.ts, 1, 3)) +} + diff --git a/tests/baselines/reference/typeGuardConstructorNarrowAny.types b/tests/baselines/reference/typeGuardConstructorNarrowAny.types new file mode 100644 index 0000000000000..a10d31a5384f0 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorNarrowAny.types @@ -0,0 +1,66 @@ +=== tests/cases/compiler/typeGuardConstructorNarrowAny.ts === +// Narrowing any to primitives +let var1: any; +>var1 : any + +if (var1.constructor === String) { +>var1.constructor === String : boolean +>var1.constructor : any +>var1 : any +>constructor : any +>String : StringConstructor + + var1; // String +>var1 : String +} +if (var1.constructor === Number) { +>var1.constructor === Number : boolean +>var1.constructor : any +>var1 : any +>constructor : any +>Number : NumberConstructor + + var1; // Number +>var1 : Number +} +if (var1.constructor === Boolean) { +>var1.constructor === Boolean : boolean +>var1.constructor : any +>var1 : any +>constructor : any +>Boolean : BooleanConstructor + + var1; // Boolean +>var1 : Boolean +} +if (var1.constructor === Array) { +>var1.constructor === Array : boolean +>var1.constructor : any +>var1 : any +>constructor : any +>Array : ArrayConstructor + + var1; // any[] +>var1 : any[] +} +if (var1.constructor === Symbol) { +>var1.constructor === Symbol : boolean +>var1.constructor : any +>var1 : any +>constructor : any +>Symbol : SymbolConstructor + + var1; // Symbol +>var1 : Symbol +} +if (var1.constructor === BigInt) { +>var1.constructor === BigInt : boolean +>var1.constructor : any +>var1 : any +>constructor : any +>BigInt : BigIntConstructor + + var1; // BigInt +>var1 : BigInt +} + diff --git a/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.js b/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.js new file mode 100644 index 0000000000000..80afeb2ad36e4 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.js @@ -0,0 +1,36 @@ +//// [typeGuardConstructorNarrowPrimitivesInUnion.ts] +// Union of primitives, number, arrays, and C1 +let var1: number | "hello" | "world" | true | false | number[] | string[]; + +if (var1.constructor === Number) { + var1; // number +} + +if (var1.constructor === String) { + var1; // "hello" | "world" +} + +if (var1.constructor === Boolean) { + var1; // boolean +} + +if (var1.constructor === Array) { + var1; // number[] | string[] +} + + +//// [typeGuardConstructorNarrowPrimitivesInUnion.js] +// Union of primitives, number, arrays, and C1 +var var1; +if (var1.constructor === Number) { + var1; // number +} +if (var1.constructor === String) { + var1; // "hello" | "world" +} +if (var1.constructor === Boolean) { + var1; // boolean +} +if (var1.constructor === Array) { + var1; // number[] | string[] +} diff --git a/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.symbols b/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.symbols new file mode 100644 index 0000000000000..1fbbdefd76c2f --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.symbols @@ -0,0 +1,45 @@ +=== tests/cases/compiler/typeGuardConstructorNarrowPrimitivesInUnion.ts === +// Union of primitives, number, arrays, and C1 +let var1: number | "hello" | "world" | true | false | number[] | string[]; +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) + +if (var1.constructor === Number) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // number +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +} + +if (var1.constructor === String) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // "hello" | "world" +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +} + +if (var1.constructor === Boolean) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // boolean +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +} + +if (var1.constructor === Array) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // number[] | string[] +>var1 : Symbol(var1, Decl(typeGuardConstructorNarrowPrimitivesInUnion.ts, 1, 3)) +} + diff --git a/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.types b/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.types new file mode 100644 index 0000000000000..619ce3b88bb21 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorNarrowPrimitivesInUnion.types @@ -0,0 +1,51 @@ +=== tests/cases/compiler/typeGuardConstructorNarrowPrimitivesInUnion.ts === +// Union of primitives, number, arrays, and C1 +let var1: number | "hello" | "world" | true | false | number[] | string[]; +>var1 : number | boolean | "hello" | "world" | number[] | string[] +>true : true +>false : false + +if (var1.constructor === Number) { +>var1.constructor === Number : boolean +>var1.constructor : Function +>var1 : number | boolean | "hello" | "world" | number[] | string[] +>constructor : Function +>Number : NumberConstructor + + var1; // number +>var1 : number +} + +if (var1.constructor === String) { +>var1.constructor === String : boolean +>var1.constructor : Function +>var1 : number | boolean | "hello" | "world" | number[] | string[] +>constructor : Function +>String : StringConstructor + + var1; // "hello" | "world" +>var1 : "hello" | "world" +} + +if (var1.constructor === Boolean) { +>var1.constructor === Boolean : boolean +>var1.constructor : Function +>var1 : number | boolean | "hello" | "world" | number[] | string[] +>constructor : Function +>Boolean : BooleanConstructor + + var1; // boolean +>var1 : boolean +} + +if (var1.constructor === Array) { +>var1.constructor === Array : boolean +>var1.constructor : Function +>var1 : number | boolean | "hello" | "world" | number[] | string[] +>constructor : Function +>Array : ArrayConstructor + + var1; // number[] | string[] +>var1 : number[] | string[] +} + diff --git a/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.js b/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.js new file mode 100644 index 0000000000000..3de66ac807a6f --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.js @@ -0,0 +1,79 @@ +//// [typeGuardConstructorPrimitiveTypes.ts] +// Narrow a union of primitive types +let var1: string | number | boolean | any[] | symbol | bigint; +if (var1.constructor === String) { + var1; // string +} +if (var1.constructor === Number) { + var1; // number +} +if (var1.constructor === Boolean) { + var1; // boolean +} +if (var1.constructor === Array) { + var1; // any[] +} +if (var1.constructor === Symbol) { + var1; // symbol +} +if (var1.constructor === BigInt) { + var1; // bigint +} + +// Narrow a union of primitive object types +let var2: String | Number | Boolean | Symbol | BigInt; +if (var2.constructor === String) { + var2; // String +} +if (var2.constructor === Number) { + var2; // Number +} +if (var2.constructor === Boolean) { + var2; // Boolean +} +if (var2.constructor === Symbol) { + var2; // Symbol +} +if (var2.constructor === BigInt) { + var2; // BigInt +} + + +//// [typeGuardConstructorPrimitiveTypes.js] +// Narrow a union of primitive types +let var1; +if (var1.constructor === String) { + var1; // string +} +if (var1.constructor === Number) { + var1; // number +} +if (var1.constructor === Boolean) { + var1; // boolean +} +if (var1.constructor === Array) { + var1; // any[] +} +if (var1.constructor === Symbol) { + var1; // symbol +} +if (var1.constructor === BigInt) { + var1; // bigint +} +// Narrow a union of primitive object types +let var2; +if (var2.constructor === String) { + var2; // String +} +if (var2.constructor === Number) { + var2; // Number +} +if (var2.constructor === Boolean) { + var2; // Boolean +} +if (var2.constructor === Symbol) { + var2; // Symbol +} +if (var2.constructor === BigInt) { + var2; // BigInt +} diff --git a/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.symbols b/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.symbols new file mode 100644 index 0000000000000..1bcc2f586eb5e --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.symbols @@ -0,0 +1,115 @@ +=== tests/cases/compiler/typeGuardConstructorPrimitiveTypes.ts === +// Narrow a union of primitive types +let var1: string | number | boolean | any[] | symbol | bigint; +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) + +if (var1.constructor === String) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --) ... and 5 more) + + var1; // string +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +} +if (var1.constructor === Number) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // number +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +} +if (var1.constructor === Boolean) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var1; // boolean +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +} +if (var1.constructor === Array) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 2 more) + + var1; // any[] +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +} +if (var1.constructor === Symbol) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) + + var1; // symbol +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +} +if (var1.constructor === BigInt) { +>var1.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>BigInt : Symbol(BigInt, Decl(lib.es2020.bigint.d.ts, --, --), Decl(lib.es2020.bigint.d.ts, --, --)) + + var1; // bigint +>var1 : Symbol(var1, Decl(typeGuardConstructorPrimitiveTypes.ts, 1, 3)) +} + +// Narrow a union of primitive object types +let var2: String | Number | Boolean | Symbol | BigInt; +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --) ... and 5 more) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) +>BigInt : Symbol(BigInt, Decl(lib.es2020.bigint.d.ts, --, --), Decl(lib.es2020.bigint.d.ts, --, --)) + +if (var2.constructor === String) { +>var2.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --) ... and 5 more) + + var2; // String +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +} +if (var2.constructor === Number) { +>var2.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var2; // Number +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +} +if (var2.constructor === Boolean) { +>var2.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + var2; // Boolean +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +} +if (var2.constructor === Symbol) { +>var2.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) + + var2; // Symbol +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +} +if (var2.constructor === BigInt) { +>var2.constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.es5.d.ts, --, --)) +>BigInt : Symbol(BigInt, Decl(lib.es2020.bigint.d.ts, --, --), Decl(lib.es2020.bigint.d.ts, --, --)) + + var2; // BigInt +>var2 : Symbol(var2, Decl(typeGuardConstructorPrimitiveTypes.ts, 22, 3)) +} + diff --git a/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.types b/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.types new file mode 100644 index 0000000000000..c23ae9e8decff --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructorPrimitiveTypes.types @@ -0,0 +1,121 @@ +=== tests/cases/compiler/typeGuardConstructorPrimitiveTypes.ts === +// Narrow a union of primitive types +let var1: string | number | boolean | any[] | symbol | bigint; +>var1 : string | number | bigint | boolean | symbol | any[] + +if (var1.constructor === String) { +>var1.constructor === String : boolean +>var1.constructor : Function +>var1 : string | number | bigint | boolean | symbol | any[] +>constructor : Function +>String : StringConstructor + + var1; // string +>var1 : string +} +if (var1.constructor === Number) { +>var1.constructor === Number : boolean +>var1.constructor : Function +>var1 : string | number | bigint | boolean | symbol | any[] +>constructor : Function +>Number : NumberConstructor + + var1; // number +>var1 : number +} +if (var1.constructor === Boolean) { +>var1.constructor === Boolean : boolean +>var1.constructor : Function +>var1 : string | number | bigint | boolean | symbol | any[] +>constructor : Function +>Boolean : BooleanConstructor + + var1; // boolean +>var1 : boolean +} +if (var1.constructor === Array) { +>var1.constructor === Array : boolean +>var1.constructor : Function +>var1 : string | number | bigint | boolean | symbol | any[] +>constructor : Function +>Array : ArrayConstructor + + var1; // any[] +>var1 : any[] +} +if (var1.constructor === Symbol) { +>var1.constructor === Symbol : boolean +>var1.constructor : Function +>var1 : string | number | bigint | boolean | symbol | any[] +>constructor : Function +>Symbol : SymbolConstructor + + var1; // symbol +>var1 : symbol +} +if (var1.constructor === BigInt) { +>var1.constructor === BigInt : boolean +>var1.constructor : Function +>var1 : string | number | bigint | boolean | symbol | any[] +>constructor : Function +>BigInt : BigIntConstructor + + var1; // bigint +>var1 : bigint +} + +// Narrow a union of primitive object types +let var2: String | Number | Boolean | Symbol | BigInt; +>var2 : String | Number | Boolean | BigInt | Symbol + +if (var2.constructor === String) { +>var2.constructor === String : boolean +>var2.constructor : Function +>var2 : String | Number | Boolean | BigInt | Symbol +>constructor : Function +>String : StringConstructor + + var2; // String +>var2 : String +} +if (var2.constructor === Number) { +>var2.constructor === Number : boolean +>var2.constructor : Function +>var2 : String | Number | Boolean | BigInt | Symbol +>constructor : Function +>Number : NumberConstructor + + var2; // Number +>var2 : Number +} +if (var2.constructor === Boolean) { +>var2.constructor === Boolean : boolean +>var2.constructor : Function +>var2 : String | Number | Boolean | BigInt | Symbol +>constructor : Function +>Boolean : BooleanConstructor + + var2; // Boolean +>var2 : Boolean +} +if (var2.constructor === Symbol) { +>var2.constructor === Symbol : boolean +>var2.constructor : Function +>var2 : String | Number | Boolean | BigInt | Symbol +>constructor : Function +>Symbol : SymbolConstructor + + var2; // Symbol +>var2 : Symbol +} +if (var2.constructor === BigInt) { +>var2.constructor === BigInt : boolean +>var2.constructor : Function +>var2 : String | Number | Boolean | BigInt | Symbol +>constructor : Function +>BigInt : BigIntConstructor + + var2; // BigInt +>var2 : BigInt +} + diff --git a/tests/cases/compiler/typeGuardConstructorClassAndNumber.ts b/tests/cases/compiler/typeGuardConstructorClassAndNumber.ts new file mode 100644 index 0000000000000..10ce6afdff05f --- /dev/null +++ b/tests/cases/compiler/typeGuardConstructorClassAndNumber.ts @@ -0,0 +1,119 @@ +// Typical case +class C1 { + property1: string; +} + +let var1: C1 | number; +if (var1.constructor == C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1["constructor"] == C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1.constructor === C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (var1["constructor"] === C1) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 == var1.constructor) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 == var1["constructor"]) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 === var1.constructor) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} +if (C1 === var1["constructor"]) { + var1; // C1 + var1.property1; // string +} +else { + var1; // number | C1 +} + +if (var1.constructor != C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1["constructor"] != C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1.constructor !== C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (var1["constructor"] !== C1) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 != var1.constructor) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 != var1["constructor"]) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 !== var1.constructor) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} +if (C1 !== var1["constructor"]) { + var1; // C1 | number + var1.property1; // error +} +else { + var1; // C1 +} diff --git a/tests/cases/compiler/typeGuardConstructorDerivedClass.ts b/tests/cases/compiler/typeGuardConstructorDerivedClass.ts new file mode 100644 index 0000000000000..5bca75c7a010b --- /dev/null +++ b/tests/cases/compiler/typeGuardConstructorDerivedClass.ts @@ -0,0 +1,65 @@ +// Derived class with different structures +class C1 { + property1: number; +} + +class C2 extends C1 { + property2: number; +} + +let var1: C2 | string; +if (var1.constructor === C1) { + var1; // never + var1.property1; // error +} +if (var1.constructor === C2) { + var1; // C2 + var1.property1; // number +} + +// Derived classes with the same structure +class C3 {} + +class C4 extends C3 {} + +let var2: C4 | string; +if (var2.constructor === C3) { + var2; // never +} +if (var2.constructor === C4) { + var2; // C4 +} + +// Disjointly structured classes +class C5 { + property1: number; +} + +class C6 { + property2: number; +} + +let let3: C6 | string; +if (let3.constructor === C5) { + let3; // never +} +if (let3.constructor === C6) { + let3; // C6 +} + +// Classes with the same structure +class C7 { + property1: number +} + +class C8 { + property1: number; +} + +let let4: C8 | string; +if (let4.constructor === C7) { + let4; // never +} +if (let4.constructor === C8) { + let4; // C8 +} diff --git a/tests/cases/compiler/typeGuardConstructorNarrowAny.ts b/tests/cases/compiler/typeGuardConstructorNarrowAny.ts new file mode 100644 index 0000000000000..4f0703dc638fb --- /dev/null +++ b/tests/cases/compiler/typeGuardConstructorNarrowAny.ts @@ -0,0 +1,23 @@ +// @target: esnext + +// Narrowing any to primitives +let var1: any; + +if (var1.constructor === String) { + var1; // String +} +if (var1.constructor === Number) { + var1; // Number +} +if (var1.constructor === Boolean) { + var1; // Boolean +} +if (var1.constructor === Array) { + var1; // any[] +} +if (var1.constructor === Symbol) { + var1; // Symbol +} +if (var1.constructor === BigInt) { + var1; // BigInt +} diff --git a/tests/cases/compiler/typeGuardConstructorNarrowPrimitivesInUnion.ts b/tests/cases/compiler/typeGuardConstructorNarrowPrimitivesInUnion.ts new file mode 100644 index 0000000000000..596b7d8416352 --- /dev/null +++ b/tests/cases/compiler/typeGuardConstructorNarrowPrimitivesInUnion.ts @@ -0,0 +1,18 @@ +// Union of primitives, number, arrays, and C1 +let var1: number | "hello" | "world" | true | false | number[] | string[]; + +if (var1.constructor === Number) { + var1; // number +} + +if (var1.constructor === String) { + var1; // "hello" | "world" +} + +if (var1.constructor === Boolean) { + var1; // boolean +} + +if (var1.constructor === Array) { + var1; // number[] | string[] +} diff --git a/tests/cases/compiler/typeGuardConstructorPrimitiveTypes.ts b/tests/cases/compiler/typeGuardConstructorPrimitiveTypes.ts new file mode 100644 index 0000000000000..9ddb2d61247e7 --- /dev/null +++ b/tests/cases/compiler/typeGuardConstructorPrimitiveTypes.ts @@ -0,0 +1,40 @@ +// @target: esnext + +// Narrow a union of primitive types +let var1: string | number | boolean | any[] | symbol | bigint; +if (var1.constructor === String) { + var1; // string +} +if (var1.constructor === Number) { + var1; // number +} +if (var1.constructor === Boolean) { + var1; // boolean +} +if (var1.constructor === Array) { + var1; // any[] +} +if (var1.constructor === Symbol) { + var1; // symbol +} +if (var1.constructor === BigInt) { + var1; // bigint +} + +// Narrow a union of primitive object types +let var2: String | Number | Boolean | Symbol | BigInt; +if (var2.constructor === String) { + var2; // String +} +if (var2.constructor === Number) { + var2; // Number +} +if (var2.constructor === Boolean) { + var2; // Boolean +} +if (var2.constructor === Symbol) { + var2; // Symbol +} +if (var2.constructor === BigInt) { + var2; // BigInt +}