Implement constructor type guard (#32774)

* Implement constructor type guard

* Fix code review issues for constructor type guard.
- Do not limit constructor expression to only identifiers
- Fix `assumeTrue` and operator no-narrow check
- Use better way to check that identifier type is a function
- Loosen restriction on what expr is left of ".constructor"
- Update typeGuardConstructorClassAndNumber test to include else cases

* Fix grammar & spacing in `narrowTypeByConstructor`

* fix bad merge

* switch (back?) to crlf

* update baselines

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
Austin Cummings 2020-03-11 13:16:33 -07:00 committed by GitHub
parent b78ef30cb7
commit ae3d28b5eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2487 additions and 0 deletions

View File

@ -20147,6 +20147,12 @@ namespace ts {
if (right.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(left)) {
return narrowTypeByTypeof(type, <TypeOfExpression>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)) {

View File

@ -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);

View File

@ -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
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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[]
}

View File

@ -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))
}

View File

@ -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[]
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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[]
}

View File

@ -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
}