diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 78ff1efc867da..90a0fcf2c5ac1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16703,6 +16703,80 @@ namespace ts { return getTypeWithFacts(mapType(type, narrowTypeForTypeofSwitch(impliedType)), switchFacts); } + function inferSubtypeTypeParameters(superType: Type, childTypeParameters: ReadonlyArray, childConstructor: Signature): Type | undefined { + // inferSubtypeTypeParameters infers the best possible parameters for the given childConstructor such that it represents every possible subtype of superType. + // An inferenceContext is created to permit us to develop inferences. + const inferenceContext = createInferenceContext( + childTypeParameters, + childConstructor, + InferenceFlags.NoDefault, // exclude defaults values from inference, since they don't participate in actual typing here. + ); + + // Infer the types from superType onto the return type of the child constructor. + const childTemplateType = getReturnTypeOfSignature(childConstructor); + inferTypes(inferenceContext.inferences, superType, childTemplateType, InferencePriority.NakedTypeVariable); + + // We extract the inferred arguments from the inference context. + // silentNeverType indicates that no type could be inferred. + // In these cases, falling back on any produces the simplest user experience. + const childArguments = getInferredTypes(inferenceContext).map(type => type === silentNeverType ? anyType : type); + + + // Lastly, supply the childType with the new parameters. + return instantiateType(childTemplateType, param => { + if (childTypeParameters.indexOf(param) >= 0) { + return childArguments[childTypeParameters.indexOf(param)]; + } + return param; + }); + } + + function narrowTypeParametersByAssignability(superType: Type, childType: Type): Type | undefined { + if (superType.flags & TypeFlags.Never) { + return undefined; + } + // Attempt to narrow `superType` to `childType`. + // If this is possible via an instantiation of childType's parameters, return the narrowed type. + // Otherwise, return undefined. + + if (!(childType.flags & TypeFlags.Object)) { + return undefined; + } + + const childObjectType = childType; + + if (!childObjectType.constructSignatures) { + // TODO: can we do anything if a construct signature doesn't exist? + return undefined; + } + + if (childObjectType.constructSignatures.length === 0) { + // TODO: can we do anything if a construct signature doesn't exist? + return undefined; + } + + // TODO: how should we select the constructor if there is more than one? + // We should probably select the most-general overload, or maybe try all of them. + const childConstructor = childObjectType.constructSignatures[0]; + + if (!childConstructor.typeParameters) { + // Narrowing via type parameter assignment is only feasible if type parameters exist. + return undefined; + } + + const childTypeParameters = childConstructor.typeParameters; + + if (superType.flags & TypeFlags.Union) { + // When the super type is a union type, it makes sense to spread across. + // If it's possible for the child type to be a subtype, then we should instead skip it. + return getUnionType( + (superType).types.map(superTypeAlternative => inferSubtypeTypeParameters(superTypeAlternative, childTypeParameters, childConstructor)).map(type => type || neverType) + ); + } + + return inferSubtypeTypeParameters(superType, childTypeParameters, childConstructor); + } + function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { const left = getReferenceCandidate(expr.left); if (!isMatchingReference(reference, left)) { @@ -16733,6 +16807,16 @@ namespace ts { if (!isTypeAny(prototypePropertyType)) { targetType = prototypePropertyType; } + + const narrowedGenericClassTypeCandidate = narrowTypeParametersByAssignability(type, rightType); + if (narrowedGenericClassTypeCandidate && !(narrowedGenericClassTypeCandidate.flags & TypeFlags.Never) && isTypeSubtypeOf(narrowedGenericClassTypeCandidate, type)) { + // When the resulting narrowed type is actually a subtype of the original, the candidate it used. + // In some cases, this fail (due to apparent non-inhabitedness). + // For example, if Child extends Parent and we narrow Parent to Child, + // we'll end up with a candidate Child which fails to be a subtype of Parent. + // In this case, we skip updating the target type. + targetType = narrowedGenericClassTypeCandidate; + } } // Don't narrow from 'any' if the target type is exactly 'Object' or 'Function' diff --git a/tests/baselines/reference/narrowGenericTypeByInstanceOf.js b/tests/baselines/reference/narrowGenericTypeByInstanceOf.js new file mode 100644 index 0000000000000..5579e6b7bb789 --- /dev/null +++ b/tests/baselines/reference/narrowGenericTypeByInstanceOf.js @@ -0,0 +1,447 @@ +//// [narrowGenericTypeByInstanceOf.ts] +// From #28560 + +function preserveParentParameters() { + class Parent { + value: T; + } + class Child extends Parent { + other: S; + } + + function withNumber(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withString(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withGeneric(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function copyParameterStructurally() { + class Parent { + value: T; + } + class Child { + value: S; + other: S; + } + + function withNumber(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withString(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withGeneric(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function useConstraint() { + // #17473 + interface Foo { + foo: string; + } + + class Bar { + constructor(readonly bar: T) {} + } + + let a: any; + if (a instanceof Bar) { + a.bar; // <-- a.bar should be 'Foo' instead of 'any' + } +} + +function enhanceConstraint() { + class Parent { + value: T; + } + class Child extends Parent { + other: T; + } + + function simpleExtends(p: Parent<1 | 2 | 3 | 4>) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function complexExtends(p: Parent<2 | 3>) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function impossibleExtends(p: Parent<3 | 4>) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function dontWidenPointlessly() { + class Query { + uses: T; + } + function f(p: T[] | Query) { + if (Array.isArray(p)) { + p; // T[], so far so good + } else if (p instanceof Query) { + p; // should be Query, not Query + } + } +} + +function multipleParameters() { + class Parent { + a: A; + b: B; + } + class Swapped extends Parent { + x: X; + y: Y; + } + function checkSwapped(p: Parent) { + if (p instanceof Swapped) { + p; + } else { + p; + } + } +} + +function inconsistentParameters() { + class Parent { + a: A; + b: B; + } + class Child extends Parent { + c: C; + } + function possible(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function impossible(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function union() { + class Parent { + a: A; + } + class Child extends Parent { + b: B; + } + function multipleParents( + p: Parent | Parent | Parent + ) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function mixedChildren(p: Parent | Child) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function imcompatibleOptions( + p: Parent | Parent | { foo: boolean } + ) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + + +//// [narrowGenericTypeByInstanceOf.js] +// From #28560 +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 __()); + }; +})(); +function preserveParentParameters() { + var Parent = /** @class */ (function () { + function Parent() { + } + return Parent; + }()); + var Child = /** @class */ (function (_super) { + __extends(Child, _super); + function Child() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Child; + }(Parent)); + function withNumber(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function withString(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function withGeneric(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } +} +function copyParameterStructurally() { + var Parent = /** @class */ (function () { + function Parent() { + } + return Parent; + }()); + var Child = /** @class */ (function () { + function Child() { + } + return Child; + }()); + function withNumber(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function withString(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function withGeneric(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } +} +function useConstraint() { + var Bar = /** @class */ (function () { + function Bar(bar) { + this.bar = bar; + } + return Bar; + }()); + var a; + if (a instanceof Bar) { + a.bar; // <-- a.bar should be 'Foo' instead of 'any' + } +} +function enhanceConstraint() { + var Parent = /** @class */ (function () { + function Parent() { + } + return Parent; + }()); + var Child = /** @class */ (function (_super) { + __extends(Child, _super); + function Child() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Child; + }(Parent)); + function simpleExtends(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function complexExtends(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function impossibleExtends(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } +} +function dontWidenPointlessly() { + var Query = /** @class */ (function () { + function Query() { + } + return Query; + }()); + function f(p) { + if (Array.isArray(p)) { + p; // T[], so far so good + } + else if (p instanceof Query) { + p; // should be Query, not Query + } + } +} +function multipleParameters() { + var Parent = /** @class */ (function () { + function Parent() { + } + return Parent; + }()); + var Swapped = /** @class */ (function (_super) { + __extends(Swapped, _super); + function Swapped() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Swapped; + }(Parent)); + function checkSwapped(p) { + if (p instanceof Swapped) { + p; + } + else { + p; + } + } +} +function inconsistentParameters() { + var Parent = /** @class */ (function () { + function Parent() { + } + return Parent; + }()); + var Child = /** @class */ (function (_super) { + __extends(Child, _super); + function Child() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Child; + }(Parent)); + function possible(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function impossible(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } +} +function union() { + var Parent = /** @class */ (function () { + function Parent() { + } + return Parent; + }()); + var Child = /** @class */ (function (_super) { + __extends(Child, _super); + function Child() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Child; + }(Parent)); + function multipleParents(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function mixedChildren(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } + function imcompatibleOptions(p) { + if (p instanceof Child) { + p; + } + else { + p; + } + } +} diff --git a/tests/baselines/reference/narrowGenericTypeByInstanceOf.symbols b/tests/baselines/reference/narrowGenericTypeByInstanceOf.symbols new file mode 100644 index 0000000000000..e8226f7bc23d9 --- /dev/null +++ b/tests/baselines/reference/narrowGenericTypeByInstanceOf.symbols @@ -0,0 +1,506 @@ +=== tests/cases/compiler/narrowGenericTypeByInstanceOf.ts === +// From #28560 + +function preserveParentParameters() { +>preserveParentParameters : Symbol(preserveParentParameters, Decl(narrowGenericTypeByInstanceOf.ts, 0, 0)) + + class Parent { +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 2, 37)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 3, 15)) + + value: T; +>value : Symbol(Parent.value, Decl(narrowGenericTypeByInstanceOf.ts, 3, 19)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 3, 15)) + } + class Child extends Parent { +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 5, 3)) +>S : Symbol(S, Decl(narrowGenericTypeByInstanceOf.ts, 6, 14)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 2, 37)) +>S : Symbol(S, Decl(narrowGenericTypeByInstanceOf.ts, 6, 14)) + + other: S; +>other : Symbol(Child.other, Decl(narrowGenericTypeByInstanceOf.ts, 6, 36)) +>S : Symbol(S, Decl(narrowGenericTypeByInstanceOf.ts, 6, 14)) + } + + function withNumber(p: Parent) { +>withNumber : Symbol(withNumber, Decl(narrowGenericTypeByInstanceOf.ts, 8, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 10, 22)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 2, 37)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 10, 22)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 5, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 10, 22)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 10, 22)) + } + } + function withString(p: Parent) { +>withString : Symbol(withString, Decl(narrowGenericTypeByInstanceOf.ts, 16, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 17, 22)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 2, 37)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 17, 22)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 5, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 17, 22)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 17, 22)) + } + } + function withGeneric(p: Parent) { +>withGeneric : Symbol(withGeneric, Decl(narrowGenericTypeByInstanceOf.ts, 23, 3)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 24, 23)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 24, 26)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 2, 37)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 24, 23)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 24, 26)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 5, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 24, 26)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 24, 26)) + } + } +} + +function copyParameterStructurally() { +>copyParameterStructurally : Symbol(copyParameterStructurally, Decl(narrowGenericTypeByInstanceOf.ts, 31, 1)) + + class Parent { +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 33, 38)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 34, 15)) + + value: T; +>value : Symbol(Parent.value, Decl(narrowGenericTypeByInstanceOf.ts, 34, 19)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 34, 15)) + } + class Child { +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 36, 3)) +>S : Symbol(S, Decl(narrowGenericTypeByInstanceOf.ts, 37, 14)) + + value: S; +>value : Symbol(Child.value, Decl(narrowGenericTypeByInstanceOf.ts, 37, 18)) +>S : Symbol(S, Decl(narrowGenericTypeByInstanceOf.ts, 37, 14)) + + other: S; +>other : Symbol(Child.other, Decl(narrowGenericTypeByInstanceOf.ts, 38, 13)) +>S : Symbol(S, Decl(narrowGenericTypeByInstanceOf.ts, 37, 14)) + } + + function withNumber(p: Parent) { +>withNumber : Symbol(withNumber, Decl(narrowGenericTypeByInstanceOf.ts, 40, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 42, 22)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 33, 38)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 42, 22)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 36, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 42, 22)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 42, 22)) + } + } + function withString(p: Parent) { +>withString : Symbol(withString, Decl(narrowGenericTypeByInstanceOf.ts, 48, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 49, 22)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 33, 38)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 49, 22)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 36, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 49, 22)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 49, 22)) + } + } + function withGeneric(p: Parent) { +>withGeneric : Symbol(withGeneric, Decl(narrowGenericTypeByInstanceOf.ts, 55, 3)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 56, 23)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 56, 26)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 33, 38)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 56, 23)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 56, 26)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 36, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 56, 26)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 56, 26)) + } + } +} + +function useConstraint() { +>useConstraint : Symbol(useConstraint, Decl(narrowGenericTypeByInstanceOf.ts, 63, 1)) + + // #17473 + interface Foo { +>Foo : Symbol(Foo, Decl(narrowGenericTypeByInstanceOf.ts, 65, 26)) + + foo: string; +>foo : Symbol(Foo.foo, Decl(narrowGenericTypeByInstanceOf.ts, 67, 17)) + } + + class Bar { +>Bar : Symbol(Bar, Decl(narrowGenericTypeByInstanceOf.ts, 69, 3)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 71, 12)) +>Foo : Symbol(Foo, Decl(narrowGenericTypeByInstanceOf.ts, 65, 26)) + + constructor(readonly bar: T) {} +>bar : Symbol(Bar.bar, Decl(narrowGenericTypeByInstanceOf.ts, 72, 16)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 71, 12)) + } + + let a: any; +>a : Symbol(a, Decl(narrowGenericTypeByInstanceOf.ts, 75, 5)) + + if (a instanceof Bar) { +>a : Symbol(a, Decl(narrowGenericTypeByInstanceOf.ts, 75, 5)) +>Bar : Symbol(Bar, Decl(narrowGenericTypeByInstanceOf.ts, 69, 3)) + + a.bar; // <-- a.bar should be 'Foo' instead of 'any' +>a.bar : Symbol(Bar.bar, Decl(narrowGenericTypeByInstanceOf.ts, 72, 16)) +>a : Symbol(a, Decl(narrowGenericTypeByInstanceOf.ts, 75, 5)) +>bar : Symbol(Bar.bar, Decl(narrowGenericTypeByInstanceOf.ts, 72, 16)) + } +} + +function enhanceConstraint() { +>enhanceConstraint : Symbol(enhanceConstraint, Decl(narrowGenericTypeByInstanceOf.ts, 79, 1)) + + class Parent { +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 81, 30)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 82, 15)) + + value: T; +>value : Symbol(Parent.value, Decl(narrowGenericTypeByInstanceOf.ts, 82, 41)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 82, 15)) + } + class Child extends Parent { +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 84, 3)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 85, 14)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 81, 30)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 85, 14)) + + other: T; +>other : Symbol(Child.other, Decl(narrowGenericTypeByInstanceOf.ts, 85, 50)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 85, 14)) + } + + function simpleExtends(p: Parent<1 | 2 | 3 | 4>) { +>simpleExtends : Symbol(simpleExtends, Decl(narrowGenericTypeByInstanceOf.ts, 87, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 89, 25)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 81, 30)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 89, 25)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 84, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 89, 25)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 89, 25)) + } + } + function complexExtends(p: Parent<2 | 3>) { +>complexExtends : Symbol(complexExtends, Decl(narrowGenericTypeByInstanceOf.ts, 95, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 96, 26)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 81, 30)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 96, 26)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 84, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 96, 26)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 96, 26)) + } + } + function impossibleExtends(p: Parent<3 | 4>) { +>impossibleExtends : Symbol(impossibleExtends, Decl(narrowGenericTypeByInstanceOf.ts, 102, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 103, 29)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 81, 30)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 103, 29)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 84, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 103, 29)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 103, 29)) + } + } +} + +function dontWidenPointlessly() { +>dontWidenPointlessly : Symbol(dontWidenPointlessly, Decl(narrowGenericTypeByInstanceOf.ts, 110, 1)) + + class Query { +>Query : Symbol(Query, Decl(narrowGenericTypeByInstanceOf.ts, 112, 33)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 113, 14)) + + uses: T; +>uses : Symbol(Query.uses, Decl(narrowGenericTypeByInstanceOf.ts, 113, 18)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 113, 14)) + } + function f(p: T[] | Query) { +>f : Symbol(f, Decl(narrowGenericTypeByInstanceOf.ts, 115, 3)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 116, 13)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 116, 16)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 116, 13)) +>Query : Symbol(Query, Decl(narrowGenericTypeByInstanceOf.ts, 112, 33)) +>T : Symbol(T, Decl(narrowGenericTypeByInstanceOf.ts, 116, 13)) + + if (Array.isArray(p)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 116, 16)) + + p; // T[], so far so good +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 116, 16)) + + } else if (p instanceof Query) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 116, 16)) +>Query : Symbol(Query, Decl(narrowGenericTypeByInstanceOf.ts, 112, 33)) + + p; // should be Query, not Query +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 116, 16)) + } + } +} + +function multipleParameters() { +>multipleParameters : Symbol(multipleParameters, Decl(narrowGenericTypeByInstanceOf.ts, 123, 1)) + + class Parent { +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 125, 31)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 126, 15)) +>B : Symbol(B, Decl(narrowGenericTypeByInstanceOf.ts, 126, 17)) + + a: A; +>a : Symbol(Parent.a, Decl(narrowGenericTypeByInstanceOf.ts, 126, 22)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 126, 15)) + + b: B; +>b : Symbol(Parent.b, Decl(narrowGenericTypeByInstanceOf.ts, 127, 9)) +>B : Symbol(B, Decl(narrowGenericTypeByInstanceOf.ts, 126, 17)) + } + class Swapped extends Parent { +>Swapped : Symbol(Swapped, Decl(narrowGenericTypeByInstanceOf.ts, 129, 3)) +>X : Symbol(X, Decl(narrowGenericTypeByInstanceOf.ts, 130, 16)) +>Y : Symbol(Y, Decl(narrowGenericTypeByInstanceOf.ts, 130, 18)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 125, 31)) +>Y : Symbol(Y, Decl(narrowGenericTypeByInstanceOf.ts, 130, 18)) +>X : Symbol(X, Decl(narrowGenericTypeByInstanceOf.ts, 130, 16)) + + x: X; +>x : Symbol(Swapped.x, Decl(narrowGenericTypeByInstanceOf.ts, 130, 44)) +>X : Symbol(X, Decl(narrowGenericTypeByInstanceOf.ts, 130, 16)) + + y: Y; +>y : Symbol(Swapped.y, Decl(narrowGenericTypeByInstanceOf.ts, 131, 9)) +>Y : Symbol(Y, Decl(narrowGenericTypeByInstanceOf.ts, 130, 18)) + } + function checkSwapped(p: Parent) { +>checkSwapped : Symbol(checkSwapped, Decl(narrowGenericTypeByInstanceOf.ts, 133, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 134, 24)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 125, 31)) + + if (p instanceof Swapped) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 134, 24)) +>Swapped : Symbol(Swapped, Decl(narrowGenericTypeByInstanceOf.ts, 129, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 134, 24)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 134, 24)) + } + } +} + +function inconsistentParameters() { +>inconsistentParameters : Symbol(inconsistentParameters, Decl(narrowGenericTypeByInstanceOf.ts, 141, 1)) + + class Parent { +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 143, 35)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 144, 15)) +>B : Symbol(B, Decl(narrowGenericTypeByInstanceOf.ts, 144, 17)) + + a: A; +>a : Symbol(Parent.a, Decl(narrowGenericTypeByInstanceOf.ts, 144, 22)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 144, 15)) + + b: B; +>b : Symbol(Parent.b, Decl(narrowGenericTypeByInstanceOf.ts, 145, 9)) +>B : Symbol(B, Decl(narrowGenericTypeByInstanceOf.ts, 144, 17)) + } + class Child extends Parent { +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 147, 3)) +>C : Symbol(C, Decl(narrowGenericTypeByInstanceOf.ts, 148, 14)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 143, 35)) +>C : Symbol(C, Decl(narrowGenericTypeByInstanceOf.ts, 148, 14)) +>C : Symbol(C, Decl(narrowGenericTypeByInstanceOf.ts, 148, 14)) + + c: C; +>c : Symbol(Child.c, Decl(narrowGenericTypeByInstanceOf.ts, 148, 39)) +>C : Symbol(C, Decl(narrowGenericTypeByInstanceOf.ts, 148, 14)) + } + function possible(p: Parent) { +>possible : Symbol(possible, Decl(narrowGenericTypeByInstanceOf.ts, 150, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 151, 20)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 143, 35)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 151, 20)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 147, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 151, 20)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 151, 20)) + } + } + function impossible(p: Parent) { +>impossible : Symbol(impossible, Decl(narrowGenericTypeByInstanceOf.ts, 157, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 158, 22)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 143, 35)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 158, 22)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 147, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 158, 22)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 158, 22)) + } + } +} + +function union() { +>union : Symbol(union, Decl(narrowGenericTypeByInstanceOf.ts, 165, 1)) + + class Parent { +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 168, 15)) + + a: A; +>a : Symbol(Parent.a, Decl(narrowGenericTypeByInstanceOf.ts, 168, 19)) +>A : Symbol(A, Decl(narrowGenericTypeByInstanceOf.ts, 168, 15)) + } + class Child extends Parent { +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 170, 3)) +>B : Symbol(B, Decl(narrowGenericTypeByInstanceOf.ts, 171, 14)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) +>B : Symbol(B, Decl(narrowGenericTypeByInstanceOf.ts, 171, 14)) + + b: B; +>b : Symbol(Child.b, Decl(narrowGenericTypeByInstanceOf.ts, 171, 36)) +>B : Symbol(B, Decl(narrowGenericTypeByInstanceOf.ts, 171, 14)) + } + function multipleParents( +>multipleParents : Symbol(multipleParents, Decl(narrowGenericTypeByInstanceOf.ts, 173, 3)) + + p: Parent | Parent | Parent +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 174, 27)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) + + ) { + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 174, 27)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 170, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 174, 27)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 174, 27)) + } + } + function mixedChildren(p: Parent | Child) { +>mixedChildren : Symbol(mixedChildren, Decl(narrowGenericTypeByInstanceOf.ts, 182, 3)) +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 183, 25)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 170, 3)) + + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 183, 25)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 170, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 183, 25)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 183, 25)) + } + } + function imcompatibleOptions( +>imcompatibleOptions : Symbol(imcompatibleOptions, Decl(narrowGenericTypeByInstanceOf.ts, 189, 3)) + + p: Parent | Parent | { foo: boolean } +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 190, 31)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) +>Parent : Symbol(Parent, Decl(narrowGenericTypeByInstanceOf.ts, 167, 18)) +>foo : Symbol(foo, Decl(narrowGenericTypeByInstanceOf.ts, 191, 42)) + + ) { + if (p instanceof Child) { +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 190, 31)) +>Child : Symbol(Child, Decl(narrowGenericTypeByInstanceOf.ts, 170, 3)) + + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 190, 31)) + + } else { + p; +>p : Symbol(p, Decl(narrowGenericTypeByInstanceOf.ts, 190, 31)) + } + } +} + diff --git a/tests/baselines/reference/narrowGenericTypeByInstanceOf.types b/tests/baselines/reference/narrowGenericTypeByInstanceOf.types new file mode 100644 index 0000000000000..c6008bf320c16 --- /dev/null +++ b/tests/baselines/reference/narrowGenericTypeByInstanceOf.types @@ -0,0 +1,452 @@ +=== tests/cases/compiler/narrowGenericTypeByInstanceOf.ts === +// From #28560 + +function preserveParentParameters() { +>preserveParentParameters : () => void + + class Parent { +>Parent : Parent + + value: T; +>value : T + } + class Child extends Parent { +>Child : Child +>Parent : Parent + + other: S; +>other : S + } + + function withNumber(p: Parent) { +>withNumber : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } + function withString(p: Parent) { +>withString : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } + function withGeneric(p: Parent) { +>withGeneric : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } +} + +function copyParameterStructurally() { +>copyParameterStructurally : () => void + + class Parent { +>Parent : Parent + + value: T; +>value : T + } + class Child { +>Child : Child + + value: S; +>value : S + + other: S; +>other : S + } + + function withNumber(p: Parent) { +>withNumber : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } + function withString(p: Parent) { +>withString : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } + function withGeneric(p: Parent) { +>withGeneric : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } +} + +function useConstraint() { +>useConstraint : () => void + + // #17473 + interface Foo { + foo: string; +>foo : string + } + + class Bar { +>Bar : Bar + + constructor(readonly bar: T) {} +>bar : T + } + + let a: any; +>a : any + + if (a instanceof Bar) { +>a instanceof Bar : boolean +>a : any +>Bar : typeof Bar + + a.bar; // <-- a.bar should be 'Foo' instead of 'any' +>a.bar : any +>a : Bar +>bar : any + } +} + +function enhanceConstraint() { +>enhanceConstraint : () => void + + class Parent { +>Parent : Parent + + value: T; +>value : T + } + class Child extends Parent { +>Child : Child +>Parent : Parent + + other: T; +>other : T + } + + function simpleExtends(p: Parent<1 | 2 | 3 | 4>) { +>simpleExtends : (p: Parent<1 | 2 | 3 | 4>) => void +>p : Parent<1 | 2 | 3 | 4> + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent<1 | 2 | 3 | 4> +>Child : typeof Child + + p; +>p : Child<1 | 2> + + } else { + p; +>p : Parent<1 | 2 | 3 | 4> + } + } + function complexExtends(p: Parent<2 | 3>) { +>complexExtends : (p: Parent<2 | 3>) => void +>p : Parent<2 | 3> + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent<2 | 3> +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent<2 | 3> + } + } + function impossibleExtends(p: Parent<3 | 4>) { +>impossibleExtends : (p: Parent<3 | 4>) => void +>p : Parent<3 | 4> + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent<3 | 4> +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent<3 | 4> + } + } +} + +function dontWidenPointlessly() { +>dontWidenPointlessly : () => void + + class Query { +>Query : Query + + uses: T; +>uses : T + } + function f(p: T[] | Query) { +>f : (p: Query | T[]) => void +>p : Query | T[] + + if (Array.isArray(p)) { +>Array.isArray(p) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>p : Query | T[] + + p; // T[], so far so good +>p : T[] + + } else if (p instanceof Query) { +>p instanceof Query : boolean +>p : Query +>Query : typeof Query + + p; // should be Query, not Query +>p : Query + } + } +} + +function multipleParameters() { +>multipleParameters : () => void + + class Parent { +>Parent : Parent + + a: A; +>a : A + + b: B; +>b : B + } + class Swapped extends Parent { +>Swapped : Swapped +>Parent : Parent + + x: X; +>x : X + + y: Y; +>y : Y + } + function checkSwapped(p: Parent) { +>checkSwapped : (p: Parent) => void +>p : Parent + + if (p instanceof Swapped) { +>p instanceof Swapped : boolean +>p : Parent +>Swapped : typeof Swapped + + p; +>p : Swapped + + } else { + p; +>p : Parent + } + } +} + +function inconsistentParameters() { +>inconsistentParameters : () => void + + class Parent { +>Parent : Parent + + a: A; +>a : A + + b: B; +>b : B + } + class Child extends Parent { +>Child : Child +>Parent : Parent + + c: C; +>c : C + } + function possible(p: Parent) { +>possible : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } + function impossible(p: Parent) { +>impossible : (p: Parent) => void +>p : Parent + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } +} + +function union() { +>union : () => void + + class Parent { +>Parent : Parent + + a: A; +>a : A + } + class Child extends Parent { +>Child : Child +>Parent : Parent + + b: B; +>b : B + } + function multipleParents( +>multipleParents : (p: Parent | Parent | Parent) => void + + p: Parent | Parent | Parent +>p : Parent | Parent | Parent + + ) { + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent | Parent | Parent +>Child : typeof Child + + p; +>p : Child | Child | Child + + } else { + p; +>p : Parent | Parent | Parent + } + } + function mixedChildren(p: Parent | Child) { +>mixedChildren : (p: Parent | Child) => void +>p : Parent | Child + + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent | Child +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent + } + } + function imcompatibleOptions( +>imcompatibleOptions : (p: Parent | Parent | { foo: boolean; }) => void + + p: Parent | Parent | { foo: boolean } +>p : Parent | Parent | { foo: boolean; } +>foo : boolean + + ) { + if (p instanceof Child) { +>p instanceof Child : boolean +>p : Parent | Parent | { foo: boolean; } +>Child : typeof Child + + p; +>p : Child + + } else { + p; +>p : Parent | Parent | { foo: boolean; } + } + } +} + diff --git a/tests/cases/compiler/narrowGenericTypeByInstanceOf.ts b/tests/cases/compiler/narrowGenericTypeByInstanceOf.ts new file mode 100644 index 0000000000000..0425a146f8b9b --- /dev/null +++ b/tests/cases/compiler/narrowGenericTypeByInstanceOf.ts @@ -0,0 +1,200 @@ +// From #28560 + +function preserveParentParameters() { + class Parent { + value: T; + } + class Child extends Parent { + other: S; + } + + function withNumber(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withString(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withGeneric(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function copyParameterStructurally() { + class Parent { + value: T; + } + class Child { + value: S; + other: S; + } + + function withNumber(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withString(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function withGeneric(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function useConstraint() { + // #17473 + interface Foo { + foo: string; + } + + class Bar { + constructor(readonly bar: T) {} + } + + let a: any; + if (a instanceof Bar) { + a.bar; // <-- a.bar should be 'Foo' instead of 'any' + } +} + +function enhanceConstraint() { + class Parent { + value: T; + } + class Child extends Parent { + other: T; + } + + function simpleExtends(p: Parent<1 | 2 | 3 | 4>) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function complexExtends(p: Parent<2 | 3>) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function impossibleExtends(p: Parent<3 | 4>) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function dontWidenPointlessly() { + class Query { + uses: T; + } + function f(p: T[] | Query) { + if (Array.isArray(p)) { + p; // T[], so far so good + } else if (p instanceof Query) { + p; // should be Query, not Query + } + } +} + +function multipleParameters() { + class Parent { + a: A; + b: B; + } + class Swapped extends Parent { + x: X; + y: Y; + } + function checkSwapped(p: Parent) { + if (p instanceof Swapped) { + p; + } else { + p; + } + } +} + +function inconsistentParameters() { + class Parent { + a: A; + b: B; + } + class Child extends Parent { + c: C; + } + function possible(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function impossible(p: Parent) { + if (p instanceof Child) { + p; + } else { + p; + } + } +} + +function union() { + class Parent { + a: A; + } + class Child extends Parent { + b: B; + } + function multipleParents( + p: Parent | Parent | Parent + ) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function mixedChildren(p: Parent | Child) { + if (p instanceof Child) { + p; + } else { + p; + } + } + function imcompatibleOptions( + p: Parent | Parent | { foo: boolean } + ) { + if (p instanceof Child) { + p; + } else { + p; + } + } +}