This commit is contained in:
Kanchalai Tanglertsampan
2016-08-08 14:01:12 -07:00
19 changed files with 1424 additions and 30 deletions

View File

@@ -8,7 +8,7 @@ node_js:
sudo: false
env:
- workerCount=4
- workerCount=3
matrix:
fast_finish: true

View File

@@ -4416,10 +4416,19 @@ namespace ts {
}
const propTypes: Type[] = [];
const declarations: Declaration[] = [];
let commonType: Type = undefined;
let hasCommonType = true;
for (const prop of props) {
if (prop.declarations) {
addRange(declarations, prop.declarations);
}
const type = getTypeOfSymbol(prop);
if (!commonType) {
commonType = type;
}
else if (type !== commonType) {
hasCommonType = false;
}
propTypes.push(getTypeOfSymbol(prop));
}
const result = <TransientSymbol>createSymbol(
@@ -4429,6 +4438,7 @@ namespace ts {
commonFlags,
name);
result.containingType = containingType;
result.hasCommonType = hasCommonType;
result.declarations = declarations;
result.isReadonly = isReadonly;
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
@@ -7798,8 +7808,39 @@ namespace ts {
return false;
}
function rootContainsMatchingReference(source: Node, target: Node) {
return target.kind === SyntaxKind.PropertyAccessExpression && containsMatchingReference(source, (<PropertyAccessExpression>target).expression);
// Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared
// type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property
// a possible discriminant if its type differs in the constituents of containing union type, and if every
// choice is a unit type or a union of unit types.
function containsMatchingReferenceDiscriminant(source: Node, target: Node) {
return target.kind === SyntaxKind.PropertyAccessExpression &&
containsMatchingReference(source, (<PropertyAccessExpression>target).expression) &&
isDiscriminantProperty(getDeclaredTypeOfReference((<PropertyAccessExpression>target).expression), (<PropertyAccessExpression>target).name.text);
}
function getDeclaredTypeOfReference(expr: Node): Type {
if (expr.kind === SyntaxKind.Identifier) {
return getTypeOfSymbol(getResolvedSymbol(<Identifier>expr));
}
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
const type = getDeclaredTypeOfReference((<PropertyAccessExpression>expr).expression);
return type && getTypeOfPropertyOfType(type, (<PropertyAccessExpression>expr).name.text);
}
return undefined;
}
function isDiscriminantProperty(type: Type, name: string) {
if (type && type.flags & TypeFlags.Union) {
const prop = getPropertyOfType(type, name);
if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
(<TransientSymbol>prop).isDiscriminantProperty = !(<TransientSymbol>prop).hasCommonType &&
isUnitUnionType(getTypeOfSymbol(prop));
}
return (<TransientSymbol>prop).isDiscriminantProperty;
}
}
return false;
}
function isOrContainsMatchingReference(source: Node, target: Node) {
@@ -8090,6 +8131,25 @@ namespace ts {
return source.flags & TypeFlags.Union ? !forEach((<UnionType>source).types, t => !contains(types, t)) : contains(types, source);
}
function isTypeSubsetOf(source: Type, target: Type) {
return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, <UnionType>target);
}
function isTypeSubsetOfUnion(source: Type, target: UnionType) {
if (source.flags & TypeFlags.Union) {
for (const t of (<UnionType>source).types) {
if (!containsType(target.types, t)) {
return false;
}
}
return true;
}
if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.Enum && (<EnumLiteralType>source).baseType === target) {
return true;
}
return containsType(target.types, source);
}
function filterType(type: Type, f: (t: Type) => boolean): Type {
return type.flags & TypeFlags.Union ?
getUnionType(filter((<UnionType>type).types, f)) :
@@ -8228,7 +8288,7 @@ namespace ts {
if (isMatchingReference(reference, expr)) {
type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
}
else if (isMatchingPropertyAccess(expr)) {
else if (isMatchingReferenceDiscriminant(expr)) {
type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
}
return createFlowType(type, isIncomplete(flowType));
@@ -8236,6 +8296,7 @@ namespace ts {
function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType {
const antecedentTypes: Type[] = [];
let subtypeReduction = false;
let seenIncomplete = false;
for (const antecedent of flow.antecedents) {
const flowType = getTypeAtFlowNode(antecedent);
@@ -8250,11 +8311,17 @@ namespace ts {
if (!contains(antecedentTypes, type)) {
antecedentTypes.push(type);
}
// If an antecedent type is not a subset of the declared type, we need to perform
// subtype reduction. This happens when a "foreign" type is injected into the control
// flow using the instanceof operator or a user defined type predicate.
if (!isTypeSubsetOf(type, declaredType)) {
subtypeReduction = true;
}
if (isIncomplete(flowType)) {
seenIncomplete = true;
}
}
return createFlowType(getUnionType(antecedentTypes), seenIncomplete);
return createFlowType(getUnionType(antecedentTypes, subtypeReduction), seenIncomplete);
}
function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType {
@@ -8280,6 +8347,7 @@ namespace ts {
// Add the flow loop junction and reference to the in-process stack and analyze
// each antecedent code path.
const antecedentTypes: Type[] = [];
let subtypeReduction = false;
flowLoopNodes[flowLoopCount] = flow;
flowLoopKeys[flowLoopCount] = key;
flowLoopTypes[flowLoopCount] = antecedentTypes;
@@ -8296,6 +8364,12 @@ namespace ts {
if (!contains(antecedentTypes, type)) {
antecedentTypes.push(type);
}
// If an antecedent type is not a subset of the declared type, we need to perform
// subtype reduction. This happens when a "foreign" type is injected into the control
// flow using the instanceof operator or a user defined type predicate.
if (!isTypeSubsetOf(type, declaredType)) {
subtypeReduction = true;
}
// If the type at a particular antecedent path is the declared type there is no
// reason to process more antecedents since the only possible outcome is subtypes
// that will be removed in the final union type anyway.
@@ -8303,13 +8377,14 @@ namespace ts {
break;
}
}
return cache[key] = getUnionType(antecedentTypes);
return cache[key] = getUnionType(antecedentTypes, subtypeReduction);
}
function isMatchingPropertyAccess(expr: Expression) {
function isMatchingReferenceDiscriminant(expr: Expression) {
return expr.kind === SyntaxKind.PropertyAccessExpression &&
declaredType.flags & TypeFlags.Union &&
isMatchingReference(reference, (<PropertyAccessExpression>expr).expression) &&
(declaredType.flags & TypeFlags.Union) !== 0;
isDiscriminantProperty(declaredType, (<PropertyAccessExpression>expr).name.text);
}
function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type {
@@ -8323,10 +8398,10 @@ namespace ts {
if (isMatchingReference(reference, expr)) {
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
}
if (isMatchingPropertyAccess(expr)) {
if (isMatchingReferenceDiscriminant(expr)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
}
if (rootContainsMatchingReference(reference, expr)) {
if (containsMatchingReferenceDiscriminant(reference, expr)) {
return declaredType;
}
return type;
@@ -8355,13 +8430,13 @@ namespace ts {
if (isMatchingReference(reference, right)) {
return narrowTypeByEquality(type, operator, left, assumeTrue);
}
if (isMatchingPropertyAccess(left)) {
if (isMatchingReferenceDiscriminant(left)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
}
if (isMatchingPropertyAccess(right)) {
if (isMatchingReferenceDiscriminant(right)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
}
if (rootContainsMatchingReference(reference, left) || rootContainsMatchingReference(reference, right)) {
if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) {
return declaredType;
}
break;
@@ -8500,9 +8575,7 @@ namespace ts {
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) {
if (!assumeTrue) {
return type.flags & TypeFlags.Union ?
getUnionType(filter((<UnionType>type).types, t => !isTypeSubtypeOf(t, candidate))) :
type;
return filterType(type, t => !isTypeSubtypeOf(t, candidate));
}
// If the current type is a union type, remove all constituents that aren't assignable to
// the candidate type. If one or more constituents remain, return a union of those.
@@ -8512,13 +8585,16 @@ namespace ts {
return getUnionType(assignableConstituents);
}
}
// If the candidate type is assignable to the target type, narrow to the candidate type.
// Otherwise, if the current type is assignable to the candidate, keep the current type.
// Otherwise, the types are completely unrelated, so narrow to the empty type.
// If the candidate type is a subtype of the target type, narrow to the candidate type.
// Otherwise, if the target type is assignable to the candidate type, keep the target type.
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
// type. Otherwise, the types are completely unrelated, so narrow to an intersection of the
// two types.
const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type;
return isTypeAssignableTo(candidate, targetType) ? candidate :
return isTypeSubtypeOf(candidate, targetType) ? candidate :
isTypeAssignableTo(type, candidate) ? type :
getIntersectionType([type, candidate]);
isTypeAssignableTo(candidate, targetType) ? candidate :
getIntersectionType([type, candidate]);
}
function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {

View File

@@ -2160,6 +2160,8 @@ namespace ts {
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
hasCommonType?: boolean; // True if constituents of synthetic property all have same type
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
resolvedExports?: SymbolTable; // Resolved exports of module
exportsChecked?: boolean; // True if exports of external module have been checked
isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration

View File

@@ -86,8 +86,8 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
sourceObj.length;
>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
}

View File

@@ -106,7 +106,7 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
sourceObj.length;
>sourceObj.length : number
>sourceObj : NodeList | HTMLCollection | ({ a: string; } & HTMLCollection)
>sourceObj : NodeList
>length : number
}

View File

@@ -0,0 +1,184 @@
//// [controlFlowInstanceof.ts]
// Repros from #10167
function f1(s: Set<string> | Set<number>) {
s = new Set<number>();
s; // Set<number>
if (s instanceof Set) {
s; // Set<number>
}
s; // Set<number>
s.add(42);
}
function f2(s: Set<string> | Set<number>) {
s = new Set<number>();
s; // Set<number>
if (s instanceof Promise) {
s; // Set<number> & Promise<any>
}
s; // Set<number>
s.add(42);
}
function f3(s: Set<string> | Set<number>) {
s; // Set<string> | Set<number>
if (s instanceof Set) {
s; // Set<string> | Set<number>
}
else {
s; // never
}
}
function f4(s: Set<string> | Set<number>) {
s = new Set<number>();
s; // Set<number>
if (s instanceof Set) {
s; // Set<number>
}
else {
s; // never
}
}
// More tests
class A { a: string }
class B extends A { b: string }
class C extends A { c: string }
function foo(x: A | undefined) {
x; // A | undefined
if (x instanceof B || x instanceof C) {
x; // B | C
}
x; // A | undefined
if (x instanceof B && x instanceof C) {
x; // B & C
}
x; // A | undefined
if (!x) {
return;
}
x; // A
if (x instanceof B) {
x; // B
if (x instanceof C) {
x; // B & C
}
else {
x; // B
}
x; // B
}
else {
x; // A
}
x; // A
}
// X is neither assignable to Y nor a subtype of Y
// Y is assignable to X, but not a subtype of X
interface X {
x?: string;
}
class Y {
y: string;
}
function goo(x: X) {
x;
if (x instanceof Y) {
x.y;
}
x;
}
//// [controlFlowInstanceof.js]
// Repros from #10167
function f1(s) {
s = new Set();
s; // Set<number>
if (s instanceof Set) {
s; // Set<number>
}
s; // Set<number>
s.add(42);
}
function f2(s) {
s = new Set();
s; // Set<number>
if (s instanceof Promise) {
s; // Set<number> & Promise<any>
}
s; // Set<number>
s.add(42);
}
function f3(s) {
s; // Set<string> | Set<number>
if (s instanceof Set) {
s; // Set<string> | Set<number>
}
else {
s; // never
}
}
function f4(s) {
s = new Set();
s; // Set<number>
if (s instanceof Set) {
s; // Set<number>
}
else {
s; // never
}
}
// More tests
class A {
}
class B extends A {
}
class C extends A {
}
function foo(x) {
x; // A | undefined
if (x instanceof B || x instanceof C) {
x; // B | C
}
x; // A | undefined
if (x instanceof B && x instanceof C) {
x; // B & C
}
x; // A | undefined
if (!x) {
return;
}
x; // A
if (x instanceof B) {
x; // B
if (x instanceof C) {
x; // B & C
}
else {
x; // B
}
x; // B
}
else {
x; // A
}
x; // A
}
class Y {
}
function goo(x) {
x;
if (x instanceof Y) {
x.y;
}
x;
}

View File

@@ -0,0 +1,232 @@
=== tests/cases/compiler/controlFlowInstanceof.ts ===
// Repros from #10167
function f1(s: Set<string> | Set<number>) {
>f1 : Symbol(f1, Decl(controlFlowInstanceof.ts, 0, 0))
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s = new Set<number>();
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s; // Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12))
if (s instanceof Set) {
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s; // Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12))
}
s; // Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12))
s.add(42);
>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --))
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12))
>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --))
}
function f2(s: Set<string> | Set<number>) {
>f2 : Symbol(f2, Decl(controlFlowInstanceof.ts, 11, 1))
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s = new Set<number>();
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s; // Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12))
if (s instanceof Promise) {
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12))
>Promise : Symbol(Promise, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --))
s; // Set<number> & Promise<any>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12))
}
s; // Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12))
s.add(42);
>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --))
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12))
>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --))
}
function f3(s: Set<string> | Set<number>) {
>f3 : Symbol(f3, Decl(controlFlowInstanceof.ts, 21, 1))
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s; // Set<string> | Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12))
if (s instanceof Set) {
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s; // Set<string> | Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12))
}
else {
s; // never
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12))
}
}
function f4(s: Set<string> | Set<number>) {
>f4 : Symbol(f4, Decl(controlFlowInstanceof.ts, 31, 1))
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s = new Set<number>();
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s; // Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12))
if (s instanceof Set) {
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12))
>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
s; // Set<number>
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12))
}
else {
s; // never
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12))
}
}
// More tests
class A { a: string }
>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1))
>a : Symbol(A.a, Decl(controlFlowInstanceof.ts, 46, 9))
class B extends A { b: string }
>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21))
>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1))
>b : Symbol(B.b, Decl(controlFlowInstanceof.ts, 47, 19))
class C extends A { c: string }
>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31))
>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1))
>c : Symbol(C.c, Decl(controlFlowInstanceof.ts, 48, 19))
function foo(x: A | undefined) {
>foo : Symbol(foo, Decl(controlFlowInstanceof.ts, 48, 31))
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1))
x; // A | undefined
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
if (x instanceof B || x instanceof C) {
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21))
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31))
x; // B | C
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
}
x; // A | undefined
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
if (x instanceof B && x instanceof C) {
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21))
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31))
x; // B & C
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
}
x; // A | undefined
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
if (!x) {
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
return;
}
x; // A
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
if (x instanceof B) {
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21))
x; // B
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
if (x instanceof C) {
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31))
x; // B & C
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
}
else {
x; // B
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
}
x; // B
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
}
else {
x; // A
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
}
x; // A
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13))
}
// X is neither assignable to Y nor a subtype of Y
// Y is assignable to X, but not a subtype of X
interface X {
>X : Symbol(X, Decl(controlFlowInstanceof.ts, 78, 1))
x?: string;
>x : Symbol(X.x, Decl(controlFlowInstanceof.ts, 83, 13))
}
class Y {
>Y : Symbol(Y, Decl(controlFlowInstanceof.ts, 85, 1))
y: string;
>y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9))
}
function goo(x: X) {
>goo : Symbol(goo, Decl(controlFlowInstanceof.ts, 89, 1))
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13))
>X : Symbol(X, Decl(controlFlowInstanceof.ts, 78, 1))
x;
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13))
if (x instanceof Y) {
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13))
>Y : Symbol(Y, Decl(controlFlowInstanceof.ts, 85, 1))
x.y;
>x.y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9))
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13))
>y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9))
}
x;
>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13))
}

View File

@@ -0,0 +1,256 @@
=== tests/cases/compiler/controlFlowInstanceof.ts ===
// Repros from #10167
function f1(s: Set<string> | Set<number>) {
>f1 : (s: Set<string> | Set<number>) => void
>s : Set<string> | Set<number>
>Set : Set<T>
>Set : Set<T>
s = new Set<number>();
>s = new Set<number>() : Set<number>
>s : Set<string> | Set<number>
>new Set<number>() : Set<number>
>Set : SetConstructor
s; // Set<number>
>s : Set<number>
if (s instanceof Set) {
>s instanceof Set : boolean
>s : Set<number>
>Set : SetConstructor
s; // Set<number>
>s : Set<number>
}
s; // Set<number>
>s : Set<number>
s.add(42);
>s.add(42) : Set<number>
>s.add : (value: number) => Set<number>
>s : Set<number>
>add : (value: number) => Set<number>
>42 : number
}
function f2(s: Set<string> | Set<number>) {
>f2 : (s: Set<string> | Set<number>) => void
>s : Set<string> | Set<number>
>Set : Set<T>
>Set : Set<T>
s = new Set<number>();
>s = new Set<number>() : Set<number>
>s : Set<string> | Set<number>
>new Set<number>() : Set<number>
>Set : SetConstructor
s; // Set<number>
>s : Set<number>
if (s instanceof Promise) {
>s instanceof Promise : boolean
>s : Set<number>
>Promise : PromiseConstructor
s; // Set<number> & Promise<any>
>s : Set<number> & Promise<any>
}
s; // Set<number>
>s : Set<number>
s.add(42);
>s.add(42) : Set<number>
>s.add : (value: number) => Set<number>
>s : Set<number>
>add : (value: number) => Set<number>
>42 : number
}
function f3(s: Set<string> | Set<number>) {
>f3 : (s: Set<string> | Set<number>) => void
>s : Set<string> | Set<number>
>Set : Set<T>
>Set : Set<T>
s; // Set<string> | Set<number>
>s : Set<string> | Set<number>
if (s instanceof Set) {
>s instanceof Set : boolean
>s : Set<string> | Set<number>
>Set : SetConstructor
s; // Set<string> | Set<number>
>s : Set<string> | Set<number>
}
else {
s; // never
>s : never
}
}
function f4(s: Set<string> | Set<number>) {
>f4 : (s: Set<string> | Set<number>) => void
>s : Set<string> | Set<number>
>Set : Set<T>
>Set : Set<T>
s = new Set<number>();
>s = new Set<number>() : Set<number>
>s : Set<string> | Set<number>
>new Set<number>() : Set<number>
>Set : SetConstructor
s; // Set<number>
>s : Set<number>
if (s instanceof Set) {
>s instanceof Set : boolean
>s : Set<number>
>Set : SetConstructor
s; // Set<number>
>s : Set<number>
}
else {
s; // never
>s : never
}
}
// More tests
class A { a: string }
>A : A
>a : string
class B extends A { b: string }
>B : B
>A : A
>b : string
class C extends A { c: string }
>C : C
>A : A
>c : string
function foo(x: A | undefined) {
>foo : (x: A) => void
>x : A
>A : A
x; // A | undefined
>x : A
if (x instanceof B || x instanceof C) {
>x instanceof B || x instanceof C : boolean
>x instanceof B : boolean
>x : A
>B : typeof B
>x instanceof C : boolean
>x : A
>C : typeof C
x; // B | C
>x : B | C
}
x; // A | undefined
>x : A
if (x instanceof B && x instanceof C) {
>x instanceof B && x instanceof C : boolean
>x instanceof B : boolean
>x : A
>B : typeof B
>x instanceof C : boolean
>x : B
>C : typeof C
x; // B & C
>x : B & C
}
x; // A | undefined
>x : A
if (!x) {
>!x : boolean
>x : A
return;
}
x; // A
>x : A
if (x instanceof B) {
>x instanceof B : boolean
>x : A
>B : typeof B
x; // B
>x : B
if (x instanceof C) {
>x instanceof C : boolean
>x : B
>C : typeof C
x; // B & C
>x : B & C
}
else {
x; // B
>x : B
}
x; // B
>x : B
}
else {
x; // A
>x : A
}
x; // A
>x : A
}
// X is neither assignable to Y nor a subtype of Y
// Y is assignable to X, but not a subtype of X
interface X {
>X : X
x?: string;
>x : string
}
class Y {
>Y : Y
y: string;
>y : string
}
function goo(x: X) {
>goo : (x: X) => void
>x : X
>X : X
x;
>x : X
if (x instanceof Y) {
>x instanceof Y : boolean
>x : X
>Y : typeof Y
x.y;
>x.y : string
>x : Y
>y : string
}
x;
>x : X
}

View File

@@ -0,0 +1,77 @@
tests/cases/compiler/discriminantPropertyCheck.ts(30,9): error TS2532: Object is possibly 'undefined'.
tests/cases/compiler/discriminantPropertyCheck.ts(66,9): error TS2532: Object is possibly 'undefined'.
==== tests/cases/compiler/discriminantPropertyCheck.ts (2 errors) ====
type Item = Item1 | Item2;
interface Base {
bar: boolean;
}
interface Item1 extends Base {
kind: "A";
foo: string | undefined;
baz: boolean;
qux: true;
}
interface Item2 extends Base {
kind: "B";
foo: string | undefined;
baz: boolean;
qux: false;
}
function goo1(x: Item) {
if (x.kind === "A" && x.foo !== undefined) {
x.foo.length;
}
}
function goo2(x: Item) {
if (x.foo !== undefined && x.kind === "A") {
x.foo.length; // Error, intervening discriminant guard
~~~~~
!!! error TS2532: Object is possibly 'undefined'.
}
}
function foo1(x: Item) {
if (x.bar && x.foo !== undefined) {
x.foo.length;
}
}
function foo2(x: Item) {
if (x.foo !== undefined && x.bar) {
x.foo.length;
}
}
function foo3(x: Item) {
if (x.baz && x.foo !== undefined) {
x.foo.length;
}
}
function foo4(x: Item) {
if (x.foo !== undefined && x.baz) {
x.foo.length;
}
}
function foo5(x: Item) {
if (x.qux && x.foo !== undefined) {
x.foo.length;
}
}
function foo6(x: Item) {
if (x.foo !== undefined && x.qux) {
x.foo.length; // Error, intervening discriminant guard
~~~~~
!!! error TS2532: Object is possibly 'undefined'.
}
}

View File

@@ -0,0 +1,111 @@
//// [discriminantPropertyCheck.ts]
type Item = Item1 | Item2;
interface Base {
bar: boolean;
}
interface Item1 extends Base {
kind: "A";
foo: string | undefined;
baz: boolean;
qux: true;
}
interface Item2 extends Base {
kind: "B";
foo: string | undefined;
baz: boolean;
qux: false;
}
function goo1(x: Item) {
if (x.kind === "A" && x.foo !== undefined) {
x.foo.length;
}
}
function goo2(x: Item) {
if (x.foo !== undefined && x.kind === "A") {
x.foo.length; // Error, intervening discriminant guard
}
}
function foo1(x: Item) {
if (x.bar && x.foo !== undefined) {
x.foo.length;
}
}
function foo2(x: Item) {
if (x.foo !== undefined && x.bar) {
x.foo.length;
}
}
function foo3(x: Item) {
if (x.baz && x.foo !== undefined) {
x.foo.length;
}
}
function foo4(x: Item) {
if (x.foo !== undefined && x.baz) {
x.foo.length;
}
}
function foo5(x: Item) {
if (x.qux && x.foo !== undefined) {
x.foo.length;
}
}
function foo6(x: Item) {
if (x.foo !== undefined && x.qux) {
x.foo.length; // Error, intervening discriminant guard
}
}
//// [discriminantPropertyCheck.js]
function goo1(x) {
if (x.kind === "A" && x.foo !== undefined) {
x.foo.length;
}
}
function goo2(x) {
if (x.foo !== undefined && x.kind === "A") {
x.foo.length; // Error, intervening discriminant guard
}
}
function foo1(x) {
if (x.bar && x.foo !== undefined) {
x.foo.length;
}
}
function foo2(x) {
if (x.foo !== undefined && x.bar) {
x.foo.length;
}
}
function foo3(x) {
if (x.baz && x.foo !== undefined) {
x.foo.length;
}
}
function foo4(x) {
if (x.foo !== undefined && x.baz) {
x.foo.length;
}
}
function foo5(x) {
if (x.qux && x.foo !== undefined) {
x.foo.length;
}
}
function foo6(x) {
if (x.foo !== undefined && x.qux) {
x.foo.length; // Error, intervening discriminant guard
}
}

View File

@@ -0,0 +1,59 @@
//// [discriminantsAndTypePredicates.ts]
// Repro from #10145
interface A { type: 'A' }
interface B { type: 'B' }
function isA(x: A | B): x is A { return x.type === 'A'; }
function isB(x: A | B): x is B { return x.type === 'B'; }
function foo1(x: A | B): any {
x; // A | B
if (isA(x)) {
return x; // A
}
x; // B
if (isB(x)) {
return x; // B
}
x; // never
}
function foo2(x: A | B): any {
x; // A | B
if (x.type === 'A') {
return x; // A
}
x; // B
if (x.type === 'B') {
return x; // B
}
x; // never
}
//// [discriminantsAndTypePredicates.js]
// Repro from #10145
function isA(x) { return x.type === 'A'; }
function isB(x) { return x.type === 'B'; }
function foo1(x) {
x; // A | B
if (isA(x)) {
return x; // A
}
x; // B
if (isB(x)) {
return x; // B
}
x; // never
}
function foo2(x) {
x; // A | B
if (x.type === 'A') {
return x; // A
}
x; // B
if (x.type === 'B') {
return x; // B
}
x; // never
}

View File

@@ -0,0 +1,94 @@
=== tests/cases/compiler/discriminantsAndTypePredicates.ts ===
// Repro from #10145
interface A { type: 'A' }
>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0))
>type : Symbol(A.type, Decl(discriminantsAndTypePredicates.ts, 2, 13))
interface B { type: 'B' }
>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25))
>type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13))
function isA(x: A | B): x is A { return x.type === 'A'; }
>isA : Symbol(isA, Decl(discriminantsAndTypePredicates.ts, 3, 25))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13))
>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0))
>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13))
>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0))
>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13))
>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13))
function isB(x: A | B): x is B { return x.type === 'B'; }
>isB : Symbol(isB, Decl(discriminantsAndTypePredicates.ts, 5, 57))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13))
>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0))
>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13))
>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25))
>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13))
>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13))
function foo1(x: A | B): any {
>foo1 : Symbol(foo1, Decl(discriminantsAndTypePredicates.ts, 6, 57))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0))
>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25))
x; // A | B
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
if (isA(x)) {
>isA : Symbol(isA, Decl(discriminantsAndTypePredicates.ts, 3, 25))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
return x; // A
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
}
x; // B
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
if (isB(x)) {
>isB : Symbol(isB, Decl(discriminantsAndTypePredicates.ts, 5, 57))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
return x; // B
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
}
x; // never
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14))
}
function foo2(x: A | B): any {
>foo2 : Symbol(foo2, Decl(discriminantsAndTypePredicates.ts, 18, 1))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0))
>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25))
x; // A | B
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
if (x.type === 'A') {
>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13))
return x; // A
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
}
x; // B
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
if (x.type === 'B') {
>x.type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13))
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
>type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13))
return x; // B
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
}
x; // never
>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14))
}

View File

@@ -0,0 +1,104 @@
=== tests/cases/compiler/discriminantsAndTypePredicates.ts ===
// Repro from #10145
interface A { type: 'A' }
>A : A
>type : "A"
interface B { type: 'B' }
>B : B
>type : "B"
function isA(x: A | B): x is A { return x.type === 'A'; }
>isA : (x: A | B) => x is A
>x : A | B
>A : A
>B : B
>x : any
>A : A
>x.type === 'A' : boolean
>x.type : "A" | "B"
>x : A | B
>type : "A" | "B"
>'A' : "A"
function isB(x: A | B): x is B { return x.type === 'B'; }
>isB : (x: A | B) => x is B
>x : A | B
>A : A
>B : B
>x : any
>B : B
>x.type === 'B' : boolean
>x.type : "A" | "B"
>x : A | B
>type : "A" | "B"
>'B' : "B"
function foo1(x: A | B): any {
>foo1 : (x: A | B) => any
>x : A | B
>A : A
>B : B
x; // A | B
>x : A | B
if (isA(x)) {
>isA(x) : boolean
>isA : (x: A | B) => x is A
>x : A | B
return x; // A
>x : A
}
x; // B
>x : B
if (isB(x)) {
>isB(x) : boolean
>isB : (x: A | B) => x is B
>x : B
return x; // B
>x : B
}
x; // never
>x : never
}
function foo2(x: A | B): any {
>foo2 : (x: A | B) => any
>x : A | B
>A : A
>B : B
x; // A | B
>x : A | B
if (x.type === 'A') {
>x.type === 'A' : boolean
>x.type : "A" | "B"
>x : A | B
>type : "A" | "B"
>'A' : "A"
return x; // A
>x : A
}
x; // B
>x : B
if (x.type === 'B') {
>x.type === 'B' : boolean
>x.type : "B"
>x : B
>type : "B"
>'B' : "B"
return x; // B
>x : B
}
x; // never
>x : never
}

View File

@@ -99,8 +99,8 @@ if (hasKind(x, "A")) {
}
else {
let b = x;
>b : A
>x : A
>b : never
>x : never
}
if (!hasKind(x, "B")) {

View File

@@ -93,8 +93,8 @@ if (hasKind(x, "A")) {
}
else {
let b = x;
>b : A
>x : A
>b : never
>x : never
}
if (!hasKind(x, "B")) {

View File

@@ -96,8 +96,8 @@ if (hasKind(x, "A")) {
}
else {
let b = x;
>b : A
>x : A
>b : never
>x : never
}
if (!hasKind(x, "B")) {

View File

@@ -0,0 +1,99 @@
// @target: es6
// Repros from #10167
function f1(s: Set<string> | Set<number>) {
s = new Set<number>();
s; // Set<number>
if (s instanceof Set) {
s; // Set<number>
}
s; // Set<number>
s.add(42);
}
function f2(s: Set<string> | Set<number>) {
s = new Set<number>();
s; // Set<number>
if (s instanceof Promise) {
s; // Set<number> & Promise<any>
}
s; // Set<number>
s.add(42);
}
function f3(s: Set<string> | Set<number>) {
s; // Set<string> | Set<number>
if (s instanceof Set) {
s; // Set<string> | Set<number>
}
else {
s; // never
}
}
function f4(s: Set<string> | Set<number>) {
s = new Set<number>();
s; // Set<number>
if (s instanceof Set) {
s; // Set<number>
}
else {
s; // never
}
}
// More tests
class A { a: string }
class B extends A { b: string }
class C extends A { c: string }
function foo(x: A | undefined) {
x; // A | undefined
if (x instanceof B || x instanceof C) {
x; // B | C
}
x; // A | undefined
if (x instanceof B && x instanceof C) {
x; // B & C
}
x; // A | undefined
if (!x) {
return;
}
x; // A
if (x instanceof B) {
x; // B
if (x instanceof C) {
x; // B & C
}
else {
x; // B
}
x; // B
}
else {
x; // A
}
x; // A
}
// X is neither assignable to Y nor a subtype of Y
// Y is assignable to X, but not a subtype of X
interface X {
x?: string;
}
class Y {
y: string;
}
function goo(x: X) {
x;
if (x instanceof Y) {
x.y;
}
x;
}

View File

@@ -0,0 +1,69 @@
// @strictNullChecks: true
type Item = Item1 | Item2;
interface Base {
bar: boolean;
}
interface Item1 extends Base {
kind: "A";
foo: string | undefined;
baz: boolean;
qux: true;
}
interface Item2 extends Base {
kind: "B";
foo: string | undefined;
baz: boolean;
qux: false;
}
function goo1(x: Item) {
if (x.kind === "A" && x.foo !== undefined) {
x.foo.length;
}
}
function goo2(x: Item) {
if (x.foo !== undefined && x.kind === "A") {
x.foo.length; // Error, intervening discriminant guard
}
}
function foo1(x: Item) {
if (x.bar && x.foo !== undefined) {
x.foo.length;
}
}
function foo2(x: Item) {
if (x.foo !== undefined && x.bar) {
x.foo.length;
}
}
function foo3(x: Item) {
if (x.baz && x.foo !== undefined) {
x.foo.length;
}
}
function foo4(x: Item) {
if (x.foo !== undefined && x.baz) {
x.foo.length;
}
}
function foo5(x: Item) {
if (x.qux && x.foo !== undefined) {
x.foo.length;
}
}
function foo6(x: Item) {
if (x.foo !== undefined && x.qux) {
x.foo.length; // Error, intervening discriminant guard
}
}

View File

@@ -0,0 +1,31 @@
// Repro from #10145
interface A { type: 'A' }
interface B { type: 'B' }
function isA(x: A | B): x is A { return x.type === 'A'; }
function isB(x: A | B): x is B { return x.type === 'B'; }
function foo1(x: A | B): any {
x; // A | B
if (isA(x)) {
return x; // A
}
x; // B
if (isB(x)) {
return x; // B
}
x; // never
}
function foo2(x: A | B): any {
x; // A | B
if (x.type === 'A') {
return x; // A
}
x; // B
if (x.type === 'B') {
return x; // B
}
x; // never
}