diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33d2dd73876b8..ff2388f88e6fa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16841,15 +16841,22 @@ namespace ts { return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); } - function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) { + function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean): Type { if (!assumeTrue) { return filterType(type, t => !isRelated(t, candidate)); } - // If the current type is a union type, remove all constituents that couldn't be instances of - // the candidate type. If one or more constituents remain, return a union of those. if (type.flags & TypeFlags.Union) { - const assignableType = filterType(type, t => isRelated(t, candidate)); - if (!(assignableType.flags & TypeFlags.Never)) { + const assignableType = candidate.flags & TypeFlags.Union + // If both the target and the candidate are union types, for each target constituent: + // - if the constituent is a subtype of the candidate, toss it in + // - if any candidate constituents are a subtype of the target constituent, toss ’em in + // - return a union of everything we tossed in. + ? mapType(type, t => getNarrowedType(candidate, t, assumeTrue, isRelated)) + // If the target type is a union type and the candidate is not, remove all + // constituents that couldn't be instances of the candidate type. If one or + // more constituents remain, return a union of those. + : filterType(type, t => isRelated(t, candidate)); + if (assignableType && !(assignableType.flags & TypeFlags.Never)) { return assignableType; } } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 147fee53393c1..daa713113c9dc 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1417,8 +1417,8 @@ namespace ts.FindAllReferences.Core { const typeNode = findAncestor(refNode, a => !isQualifiedName(a.parent) && !isTypeNode(a.parent) && !isTypeElement(a.parent))!; const typeHavingNode = typeNode.parent; if (hasType(typeHavingNode) && typeHavingNode.type === typeNode && state.markSeenContainingTypeReference(typeHavingNode)) { - if (hasInitializer(typeHavingNode)) { - addIfImplementation(typeHavingNode.initializer!); + if (hasInitializer(typeHavingNode) && isExpression(typeHavingNode.initializer!)) { + addIfImplementation(typeHavingNode.initializer); } else if (isFunctionLike(typeHavingNode) && (typeHavingNode as FunctionLikeDeclaration).body) { const body = (typeHavingNode as FunctionLikeDeclaration).body!; diff --git a/tests/baselines/reference/partiallyDiscriminantedUnions.types b/tests/baselines/reference/partiallyDiscriminantedUnions.types index f1d44249c0eb2..7ccd36c2379e6 100644 --- a/tests/baselines/reference/partiallyDiscriminantedUnions.types +++ b/tests/baselines/reference/partiallyDiscriminantedUnions.types @@ -95,13 +95,13 @@ function fail(s: Shapes) { if (s.kind === "circle") { >s.kind === "circle" : boolean >s.kind : "square" | "circle" ->s : Shape +>s : Square | Circle | (Square & Shape[]) | (Circle & Shape[]) >kind : "square" | "circle" >"circle" : "circle" let c: Circle = s; >c : Circle ->s : Circle +>s : Circle | (Circle & Shape[]) } } } diff --git a/tests/baselines/reference/stringLiteralCheckedInIf02.types b/tests/baselines/reference/stringLiteralCheckedInIf02.types index 8056c2dd43a27..a3666a0ddab7c 100644 --- a/tests/baselines/reference/stringLiteralCheckedInIf02.types +++ b/tests/baselines/reference/stringLiteralCheckedInIf02.types @@ -29,7 +29,7 @@ function f(foo: T) { >foo : T return foo; ->foo : S +>foo : "a" | "b" | ("a" & S[]) | ("b" & S[]) } else { return foo[0]; diff --git a/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.js b/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.js new file mode 100644 index 0000000000000..1d5f35d86594a --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.js @@ -0,0 +1,68 @@ +//// [typeGuardNarrowsEmptyStringOrUndefinedUnion.ts] +declare let a: string | undefined; + +declare function isEmptyStringOrUndefined(a: string | undefined): a is "" | undefined; +if (isEmptyStringOrUndefined(a)) { + a; // "" | undefined +} + +declare function isEmptyStringOrFoo(a: any): a is "" | "foo"; +if (isEmptyStringOrFoo(a)) { + a; // "" | "foo" +} + +declare function isNumberOrBoolean(a: any): a is number | boolean; +if (isNumberOrBoolean(a)) { + a; // never +} + +declare let b: "" | undefined; + +declare function isStringOrUndefined(b: any): b is string | undefined; +if (isStringOrUndefined(b)) { + b; // "" | undefined +} + +if (isNumberOrBoolean(b)) { + b; // never +} + +type A = { a: unknown }; +type B = { b: unknown }; +declare let c: { a: string } | { z: number }; + +declare function isAorB(c: any): c is A | B; +if (isAorB(c)) { + c; // { a: string } +} + +declare let d: A | B; + +declare function hasStringPropertyAOrIsBOrUndefined(d: any): d is { a: string } | B | undefined; +if (hasStringPropertyAOrIsBOrUndefined(d)) { + d; // { a: string } | B +} + + +//// [typeGuardNarrowsEmptyStringOrUndefinedUnion.js] +if (isEmptyStringOrUndefined(a)) { + a; // "" | undefined +} +if (isEmptyStringOrFoo(a)) { + a; // "" | "foo" +} +if (isNumberOrBoolean(a)) { + a; // never +} +if (isStringOrUndefined(b)) { + b; // "" | undefined +} +if (isNumberOrBoolean(b)) { + b; // never +} +if (isAorB(c)) { + c; // { a: string } +} +if (hasStringPropertyAOrIsBOrUndefined(d)) { + d; // { a: string } | B +} diff --git a/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.symbols b/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.symbols new file mode 100644 index 0000000000000..5c68762c06f39 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.symbols @@ -0,0 +1,115 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsEmptyStringOrUndefinedUnion.ts === +declare let a: string | undefined; +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 11)) + +declare function isEmptyStringOrUndefined(a: string | undefined): a is "" | undefined; +>isEmptyStringOrUndefined : Symbol(isEmptyStringOrUndefined, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 34)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 2, 42)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 2, 42)) + +if (isEmptyStringOrUndefined(a)) { +>isEmptyStringOrUndefined : Symbol(isEmptyStringOrUndefined, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 34)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 11)) + + a; // "" | undefined +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 11)) +} + +declare function isEmptyStringOrFoo(a: any): a is "" | "foo"; +>isEmptyStringOrFoo : Symbol(isEmptyStringOrFoo, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 5, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 7, 36)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 7, 36)) + +if (isEmptyStringOrFoo(a)) { +>isEmptyStringOrFoo : Symbol(isEmptyStringOrFoo, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 5, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 11)) + + a; // "" | "foo" +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 11)) +} + +declare function isNumberOrBoolean(a: any): a is number | boolean; +>isNumberOrBoolean : Symbol(isNumberOrBoolean, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 10, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 12, 35)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 12, 35)) + +if (isNumberOrBoolean(a)) { +>isNumberOrBoolean : Symbol(isNumberOrBoolean, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 10, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 11)) + + a; // never +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 0, 11)) +} + +declare let b: "" | undefined; +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 17, 11)) + +declare function isStringOrUndefined(b: any): b is string | undefined; +>isStringOrUndefined : Symbol(isStringOrUndefined, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 17, 30)) +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 19, 37)) +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 19, 37)) + +if (isStringOrUndefined(b)) { +>isStringOrUndefined : Symbol(isStringOrUndefined, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 17, 30)) +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 17, 11)) + + b; // "" | undefined +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 17, 11)) +} + +if (isNumberOrBoolean(b)) { +>isNumberOrBoolean : Symbol(isNumberOrBoolean, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 10, 1)) +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 17, 11)) + + b; // never +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 17, 11)) +} + +type A = { a: unknown }; +>A : Symbol(A, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 26, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 28, 10)) + +type B = { b: unknown }; +>B : Symbol(B, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 28, 24)) +>b : Symbol(b, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 29, 10)) + +declare let c: { a: string } | { z: number }; +>c : Symbol(c, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 30, 11)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 30, 16)) +>z : Symbol(z, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 30, 32)) + +declare function isAorB(c: any): c is A | B; +>isAorB : Symbol(isAorB, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 30, 45)) +>c : Symbol(c, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 32, 24)) +>c : Symbol(c, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 32, 24)) +>A : Symbol(A, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 26, 1)) +>B : Symbol(B, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 28, 24)) + +if (isAorB(c)) { +>isAorB : Symbol(isAorB, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 30, 45)) +>c : Symbol(c, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 30, 11)) + + c; // { a: string } +>c : Symbol(c, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 30, 11)) +} + +declare let d: A | B; +>d : Symbol(d, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 37, 11)) +>A : Symbol(A, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 26, 1)) +>B : Symbol(B, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 28, 24)) + +declare function hasStringPropertyAOrIsBOrUndefined(d: any): d is { a: string } | B | undefined; +>hasStringPropertyAOrIsBOrUndefined : Symbol(hasStringPropertyAOrIsBOrUndefined, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 37, 21)) +>d : Symbol(d, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 39, 52)) +>d : Symbol(d, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 39, 52)) +>a : Symbol(a, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 39, 67)) +>B : Symbol(B, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 28, 24)) + +if (hasStringPropertyAOrIsBOrUndefined(d)) { +>hasStringPropertyAOrIsBOrUndefined : Symbol(hasStringPropertyAOrIsBOrUndefined, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 37, 21)) +>d : Symbol(d, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 37, 11)) + + d; // { a: string } | B +>d : Symbol(d, Decl(typeGuardNarrowsEmptyStringOrUndefinedUnion.ts, 37, 11)) +} + diff --git a/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.types b/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.types new file mode 100644 index 0000000000000..75059b2c20c53 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsEmptyStringOrUndefinedUnion.types @@ -0,0 +1,111 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsEmptyStringOrUndefinedUnion.ts === +declare let a: string | undefined; +>a : string | undefined + +declare function isEmptyStringOrUndefined(a: string | undefined): a is "" | undefined; +>isEmptyStringOrUndefined : (a: string | undefined) => a is "" | undefined +>a : string | undefined + +if (isEmptyStringOrUndefined(a)) { +>isEmptyStringOrUndefined(a) : boolean +>isEmptyStringOrUndefined : (a: string | undefined) => a is "" | undefined +>a : string | undefined + + a; // "" | undefined +>a : "" | undefined +} + +declare function isEmptyStringOrFoo(a: any): a is "" | "foo"; +>isEmptyStringOrFoo : (a: any) => a is "" | "foo" +>a : any + +if (isEmptyStringOrFoo(a)) { +>isEmptyStringOrFoo(a) : boolean +>isEmptyStringOrFoo : (a: any) => a is "" | "foo" +>a : string | undefined + + a; // "" | "foo" +>a : "" | "foo" +} + +declare function isNumberOrBoolean(a: any): a is number | boolean; +>isNumberOrBoolean : (a: any) => a is number | boolean +>a : any + +if (isNumberOrBoolean(a)) { +>isNumberOrBoolean(a) : boolean +>isNumberOrBoolean : (a: any) => a is number | boolean +>a : string | undefined + + a; // never +>a : never +} + +declare let b: "" | undefined; +>b : "" | undefined + +declare function isStringOrUndefined(b: any): b is string | undefined; +>isStringOrUndefined : (b: any) => b is string | undefined +>b : any + +if (isStringOrUndefined(b)) { +>isStringOrUndefined(b) : boolean +>isStringOrUndefined : (b: any) => b is string | undefined +>b : "" | undefined + + b; // "" | undefined +>b : "" | undefined +} + +if (isNumberOrBoolean(b)) { +>isNumberOrBoolean(b) : boolean +>isNumberOrBoolean : (a: any) => a is number | boolean +>b : "" | undefined + + b; // never +>b : never +} + +type A = { a: unknown }; +>A : A +>a : unknown + +type B = { b: unknown }; +>B : B +>b : unknown + +declare let c: { a: string } | { z: number }; +>c : { a: string; } | { z: number; } +>a : string +>z : number + +declare function isAorB(c: any): c is A | B; +>isAorB : (c: any) => c is A | B +>c : any + +if (isAorB(c)) { +>isAorB(c) : boolean +>isAorB : (c: any) => c is A | B +>c : { a: string; } | { z: number; } + + c; // { a: string } +>c : { a: string; } +} + +declare let d: A | B; +>d : A | B + +declare function hasStringPropertyAOrIsBOrUndefined(d: any): d is { a: string } | B | undefined; +>hasStringPropertyAOrIsBOrUndefined : (d: any) => d is B | { a: string; } | undefined +>d : any +>a : string + +if (hasStringPropertyAOrIsBOrUndefined(d)) { +>hasStringPropertyAOrIsBOrUndefined(d) : boolean +>hasStringPropertyAOrIsBOrUndefined : (d: any) => d is B | { a: string; } | undefined +>d : A | B + + d; // { a: string } | B +>d : B | { a: string; } +} + diff --git a/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.js b/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.js new file mode 100644 index 0000000000000..3771f6a11182d --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.js @@ -0,0 +1,77 @@ +//// [typeGuardNarrowsUnionWithUnion.ts] +declare let a: string | undefined; + +declare function isEmptyStringOrUndefined(a: string | undefined): a is "" | undefined; +if (isEmptyStringOrUndefined(a)) { + a; // "" | undefined +} + +declare function isEmptyStringOrFoo(a: any): a is "" | "foo"; +if (isEmptyStringOrFoo(a)) { + a; // "" | "foo" +} + +declare function isNumberOrBoolean(a: any): a is number | boolean; +if (isNumberOrBoolean(a)) { + a; // never +} + +declare let b: "" | undefined; + +declare function isStringOrUndefined(b: any): b is string | undefined; +if (isStringOrUndefined(b)) { + b; // "" | undefined +} + +if (isNumberOrBoolean(b)) { + b; // never +} + +type A = { a: unknown }; +type B = { b: unknown }; +declare let c: { a: string } | { z: number }; + +declare function isAorB(c: any): c is A | B; +if (isAorB(c)) { + c; // { a: string } +} + +declare let d: A | B; + +declare function hasStringPropertyAOrIsBOrUndefined(d: any): d is { a: string } | B | undefined; +if (hasStringPropertyAOrIsBOrUndefined(d)) { + d; // { a: string } | B +} + +declare let e: { x: number } | { y: string }; +declare function isEightOrString(n: any): n is { x: 8 } | { x: string } +if (isEightOrString(e)) { + e; +} + + +//// [typeGuardNarrowsUnionWithUnion.js] +if (isEmptyStringOrUndefined(a)) { + a; // "" | undefined +} +if (isEmptyStringOrFoo(a)) { + a; // "" | "foo" +} +if (isNumberOrBoolean(a)) { + a; // never +} +if (isStringOrUndefined(b)) { + b; // "" | undefined +} +if (isNumberOrBoolean(b)) { + b; // never +} +if (isAorB(c)) { + c; // { a: string } +} +if (hasStringPropertyAOrIsBOrUndefined(d)) { + d; // { a: string } | B +} +if (isEightOrString(e)) { + e; +} diff --git a/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.symbols b/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.symbols new file mode 100644 index 0000000000000..6cb37f59888f7 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.symbols @@ -0,0 +1,135 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsUnionWithUnion.ts === +declare let a: string | undefined; +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 11)) + +declare function isEmptyStringOrUndefined(a: string | undefined): a is "" | undefined; +>isEmptyStringOrUndefined : Symbol(isEmptyStringOrUndefined, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 34)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 2, 42)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 2, 42)) + +if (isEmptyStringOrUndefined(a)) { +>isEmptyStringOrUndefined : Symbol(isEmptyStringOrUndefined, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 34)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 11)) + + a; // "" | undefined +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 11)) +} + +declare function isEmptyStringOrFoo(a: any): a is "" | "foo"; +>isEmptyStringOrFoo : Symbol(isEmptyStringOrFoo, Decl(typeGuardNarrowsUnionWithUnion.ts, 5, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 7, 36)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 7, 36)) + +if (isEmptyStringOrFoo(a)) { +>isEmptyStringOrFoo : Symbol(isEmptyStringOrFoo, Decl(typeGuardNarrowsUnionWithUnion.ts, 5, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 11)) + + a; // "" | "foo" +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 11)) +} + +declare function isNumberOrBoolean(a: any): a is number | boolean; +>isNumberOrBoolean : Symbol(isNumberOrBoolean, Decl(typeGuardNarrowsUnionWithUnion.ts, 10, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 12, 35)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 12, 35)) + +if (isNumberOrBoolean(a)) { +>isNumberOrBoolean : Symbol(isNumberOrBoolean, Decl(typeGuardNarrowsUnionWithUnion.ts, 10, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 11)) + + a; // never +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 0, 11)) +} + +declare let b: "" | undefined; +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 17, 11)) + +declare function isStringOrUndefined(b: any): b is string | undefined; +>isStringOrUndefined : Symbol(isStringOrUndefined, Decl(typeGuardNarrowsUnionWithUnion.ts, 17, 30)) +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 19, 37)) +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 19, 37)) + +if (isStringOrUndefined(b)) { +>isStringOrUndefined : Symbol(isStringOrUndefined, Decl(typeGuardNarrowsUnionWithUnion.ts, 17, 30)) +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 17, 11)) + + b; // "" | undefined +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 17, 11)) +} + +if (isNumberOrBoolean(b)) { +>isNumberOrBoolean : Symbol(isNumberOrBoolean, Decl(typeGuardNarrowsUnionWithUnion.ts, 10, 1)) +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 17, 11)) + + b; // never +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 17, 11)) +} + +type A = { a: unknown }; +>A : Symbol(A, Decl(typeGuardNarrowsUnionWithUnion.ts, 26, 1)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 28, 10)) + +type B = { b: unknown }; +>B : Symbol(B, Decl(typeGuardNarrowsUnionWithUnion.ts, 28, 24)) +>b : Symbol(b, Decl(typeGuardNarrowsUnionWithUnion.ts, 29, 10)) + +declare let c: { a: string } | { z: number }; +>c : Symbol(c, Decl(typeGuardNarrowsUnionWithUnion.ts, 30, 11)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 30, 16)) +>z : Symbol(z, Decl(typeGuardNarrowsUnionWithUnion.ts, 30, 32)) + +declare function isAorB(c: any): c is A | B; +>isAorB : Symbol(isAorB, Decl(typeGuardNarrowsUnionWithUnion.ts, 30, 45)) +>c : Symbol(c, Decl(typeGuardNarrowsUnionWithUnion.ts, 32, 24)) +>c : Symbol(c, Decl(typeGuardNarrowsUnionWithUnion.ts, 32, 24)) +>A : Symbol(A, Decl(typeGuardNarrowsUnionWithUnion.ts, 26, 1)) +>B : Symbol(B, Decl(typeGuardNarrowsUnionWithUnion.ts, 28, 24)) + +if (isAorB(c)) { +>isAorB : Symbol(isAorB, Decl(typeGuardNarrowsUnionWithUnion.ts, 30, 45)) +>c : Symbol(c, Decl(typeGuardNarrowsUnionWithUnion.ts, 30, 11)) + + c; // { a: string } +>c : Symbol(c, Decl(typeGuardNarrowsUnionWithUnion.ts, 30, 11)) +} + +declare let d: A | B; +>d : Symbol(d, Decl(typeGuardNarrowsUnionWithUnion.ts, 37, 11)) +>A : Symbol(A, Decl(typeGuardNarrowsUnionWithUnion.ts, 26, 1)) +>B : Symbol(B, Decl(typeGuardNarrowsUnionWithUnion.ts, 28, 24)) + +declare function hasStringPropertyAOrIsBOrUndefined(d: any): d is { a: string } | B | undefined; +>hasStringPropertyAOrIsBOrUndefined : Symbol(hasStringPropertyAOrIsBOrUndefined, Decl(typeGuardNarrowsUnionWithUnion.ts, 37, 21)) +>d : Symbol(d, Decl(typeGuardNarrowsUnionWithUnion.ts, 39, 52)) +>d : Symbol(d, Decl(typeGuardNarrowsUnionWithUnion.ts, 39, 52)) +>a : Symbol(a, Decl(typeGuardNarrowsUnionWithUnion.ts, 39, 67)) +>B : Symbol(B, Decl(typeGuardNarrowsUnionWithUnion.ts, 28, 24)) + +if (hasStringPropertyAOrIsBOrUndefined(d)) { +>hasStringPropertyAOrIsBOrUndefined : Symbol(hasStringPropertyAOrIsBOrUndefined, Decl(typeGuardNarrowsUnionWithUnion.ts, 37, 21)) +>d : Symbol(d, Decl(typeGuardNarrowsUnionWithUnion.ts, 37, 11)) + + d; // { a: string } | B +>d : Symbol(d, Decl(typeGuardNarrowsUnionWithUnion.ts, 37, 11)) +} + +declare let e: { x: number } | { y: string }; +>e : Symbol(e, Decl(typeGuardNarrowsUnionWithUnion.ts, 44, 11)) +>x : Symbol(x, Decl(typeGuardNarrowsUnionWithUnion.ts, 44, 16)) +>y : Symbol(y, Decl(typeGuardNarrowsUnionWithUnion.ts, 44, 32)) + +declare function isEightOrString(n: any): n is { x: 8 } | { x: string } +>isEightOrString : Symbol(isEightOrString, Decl(typeGuardNarrowsUnionWithUnion.ts, 44, 45)) +>n : Symbol(n, Decl(typeGuardNarrowsUnionWithUnion.ts, 45, 33)) +>n : Symbol(n, Decl(typeGuardNarrowsUnionWithUnion.ts, 45, 33)) +>x : Symbol(x, Decl(typeGuardNarrowsUnionWithUnion.ts, 45, 48)) +>x : Symbol(x, Decl(typeGuardNarrowsUnionWithUnion.ts, 45, 59)) + +if (isEightOrString(e)) { +>isEightOrString : Symbol(isEightOrString, Decl(typeGuardNarrowsUnionWithUnion.ts, 44, 45)) +>e : Symbol(e, Decl(typeGuardNarrowsUnionWithUnion.ts, 44, 11)) + + e; +>e : Symbol(e, Decl(typeGuardNarrowsUnionWithUnion.ts, 44, 11)) +} + diff --git a/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.types b/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.types new file mode 100644 index 0000000000000..eb58bd0c0bd73 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsUnionWithUnion.types @@ -0,0 +1,131 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsUnionWithUnion.ts === +declare let a: string | undefined; +>a : string | undefined + +declare function isEmptyStringOrUndefined(a: string | undefined): a is "" | undefined; +>isEmptyStringOrUndefined : (a: string | undefined) => a is "" | undefined +>a : string | undefined + +if (isEmptyStringOrUndefined(a)) { +>isEmptyStringOrUndefined(a) : boolean +>isEmptyStringOrUndefined : (a: string | undefined) => a is "" | undefined +>a : string | undefined + + a; // "" | undefined +>a : "" | undefined +} + +declare function isEmptyStringOrFoo(a: any): a is "" | "foo"; +>isEmptyStringOrFoo : (a: any) => a is "" | "foo" +>a : any + +if (isEmptyStringOrFoo(a)) { +>isEmptyStringOrFoo(a) : boolean +>isEmptyStringOrFoo : (a: any) => a is "" | "foo" +>a : string | undefined + + a; // "" | "foo" +>a : "" | "foo" +} + +declare function isNumberOrBoolean(a: any): a is number | boolean; +>isNumberOrBoolean : (a: any) => a is number | boolean +>a : any + +if (isNumberOrBoolean(a)) { +>isNumberOrBoolean(a) : boolean +>isNumberOrBoolean : (a: any) => a is number | boolean +>a : string | undefined + + a; // never +>a : never +} + +declare let b: "" | undefined; +>b : "" | undefined + +declare function isStringOrUndefined(b: any): b is string | undefined; +>isStringOrUndefined : (b: any) => b is string | undefined +>b : any + +if (isStringOrUndefined(b)) { +>isStringOrUndefined(b) : boolean +>isStringOrUndefined : (b: any) => b is string | undefined +>b : "" | undefined + + b; // "" | undefined +>b : "" | undefined +} + +if (isNumberOrBoolean(b)) { +>isNumberOrBoolean(b) : boolean +>isNumberOrBoolean : (a: any) => a is number | boolean +>b : "" | undefined + + b; // never +>b : never +} + +type A = { a: unknown }; +>A : A +>a : unknown + +type B = { b: unknown }; +>B : B +>b : unknown + +declare let c: { a: string } | { z: number }; +>c : { a: string; } | { z: number; } +>a : string +>z : number + +declare function isAorB(c: any): c is A | B; +>isAorB : (c: any) => c is A | B +>c : any + +if (isAorB(c)) { +>isAorB(c) : boolean +>isAorB : (c: any) => c is A | B +>c : { a: string; } | { z: number; } + + c; // { a: string } +>c : { a: string; } | (A & { z: number; }) | (B & { z: number; }) +} + +declare let d: A | B; +>d : A | B + +declare function hasStringPropertyAOrIsBOrUndefined(d: any): d is { a: string } | B | undefined; +>hasStringPropertyAOrIsBOrUndefined : (d: any) => d is B | { a: string; } | undefined +>d : any +>a : string + +if (hasStringPropertyAOrIsBOrUndefined(d)) { +>hasStringPropertyAOrIsBOrUndefined(d) : boolean +>hasStringPropertyAOrIsBOrUndefined : (d: any) => d is B | { a: string; } | undefined +>d : A | B + + d; // { a: string } | B +>d : B | { a: string; } +} + +declare let e: { x: number } | { y: string }; +>e : { x: number; } | { y: string; } +>x : number +>y : string + +declare function isEightOrString(n: any): n is { x: 8 } | { x: string } +>isEightOrString : (n: any) => n is { x: 8; } | { x: string; } +>n : any +>x : 8 +>x : string + +if (isEightOrString(e)) { +>isEightOrString(e) : boolean +>isEightOrString : (n: any) => n is { x: 8; } | { x: string; } +>e : { x: number; } | { y: string; } + + e; +>e : { x: 8; } | ({ x: 8; } & { y: string; }) | ({ x: string; } & { y: string; }) +} + diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt index a15eed1a8683e..17248e9f6641c 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt @@ -3,14 +3,20 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(33,5): error TS2322: Type '"str"' is not assignable to type 'number'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(34,10): error TS2339: Property 'bar' does not exist on type 'B'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(41,10): error TS2339: Property 'bar' does not exist on type 'B'. -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(66,10): error TS2339: Property 'bar2' does not exist on type 'C1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(65,10): error TS2339: Property 'bar1' does not exist on type 'C1 | C2'. + Property 'bar1' does not exist on type 'C2'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(66,10): error TS2339: Property 'bar2' does not exist on type 'C1 | C2'. + Property 'bar2' does not exist on type 'C1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(72,10): error TS2339: Property 'bar1' does not exist on type 'C1 | C2'. Property 'bar1' does not exist on type 'C2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(73,10): error TS2339: Property 'bar2' does not exist on type 'C1 | C2'. Property 'bar2' does not exist on type 'C1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(85,10): error TS2339: Property 'bar' does not exist on type 'D'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(91,10): error TS2339: Property 'bar' does not exist on type 'D'. -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(112,10): error TS2339: Property 'bar2' does not exist on type 'E1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(111,10): error TS2339: Property 'bar1' does not exist on type 'E1 | E2'. + Property 'bar1' does not exist on type 'E2'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(112,10): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. + Property 'bar2' does not exist on type 'E1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(118,11): error TS2339: Property 'bar1' does not exist on type 'E1 | E2'. Property 'bar1' does not exist on type 'E2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. @@ -26,7 +32,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(188,11): error TS2551: Property 'foo2' does not exist on type 'H'. Did you mean 'foo'? -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (20 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (22 errors) ==== interface AConstructor { new (): A; } @@ -102,9 +108,13 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru obj5.foo; obj5.c; obj5.bar1; + ~~~~ +!!! error TS2339: Property 'bar1' does not exist on type 'C1 | C2'. +!!! error TS2339: Property 'bar1' does not exist on type 'C2'. obj5.bar2; ~~~~ -!!! error TS2339: Property 'bar2' does not exist on type 'C1'. +!!! error TS2339: Property 'bar2' does not exist on type 'C1 | C2'. +!!! error TS2339: Property 'bar2' does not exist on type 'C1'. } var obj6: any; @@ -160,9 +170,13 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru if (obj9 instanceof E) { // narrowed to E1 | E2 obj9.foo; obj9.bar1; + ~~~~ +!!! error TS2339: Property 'bar1' does not exist on type 'E1 | E2'. +!!! error TS2339: Property 'bar1' does not exist on type 'E2'. obj9.bar2; ~~~~ -!!! error TS2339: Property 'bar2' does not exist on type 'E1'. +!!! error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. +!!! error TS2339: Property 'bar2' does not exist on type 'E1'. } var obj10: any; diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.symbols b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.symbols index 7856be224913c..26c9d0b090d47 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.symbols +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.symbols @@ -162,19 +162,17 @@ if (obj5 instanceof C) { // narrowed to C1|C2. >C : Symbol(C, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 58, 11)) obj5.foo; ->obj5.foo : Symbol(C1.foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 48, 14)) +>obj5.foo : Symbol(foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 48, 14), Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 53, 14)) >obj5 : Symbol(obj5, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 60, 3)) ->foo : Symbol(C1.foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 48, 14)) +>foo : Symbol(foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 48, 14), Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 53, 14)) obj5.c; ->obj5.c : Symbol(C1.c, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 49, 16)) +>obj5.c : Symbol(c, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 49, 16), Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 54, 16)) >obj5 : Symbol(obj5, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 60, 3)) ->c : Symbol(C1.c, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 49, 16)) +>c : Symbol(c, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 49, 16), Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 54, 16)) obj5.bar1; ->obj5.bar1 : Symbol(C1.bar1, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 50, 14)) >obj5 : Symbol(obj5, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 60, 3)) ->bar1 : Symbol(C1.bar1, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 50, 14)) obj5.bar2; >obj5 : Symbol(obj5, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 60, 3)) @@ -283,14 +281,12 @@ if (obj9 instanceof E) { // narrowed to E1 | E2 >E : Symbol(E, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 105, 11)) obj9.foo; ->obj9.foo : Symbol(E1.foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 97, 14)) +>obj9.foo : Symbol(foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 97, 14), Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 101, 14)) >obj9 : Symbol(obj9, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 107, 3)) ->foo : Symbol(E1.foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 97, 14)) +>foo : Symbol(foo, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 97, 14), Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 101, 14)) obj9.bar1; ->obj9.bar1 : Symbol(E1.bar1, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 98, 16)) >obj9 : Symbol(obj9, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 107, 3)) ->bar1 : Symbol(E1.bar1, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 98, 16)) obj9.bar2; >obj9 : Symbol(obj9, Decl(typeGuardsWithInstanceOfByConstructorSignature.ts, 107, 3)) diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types index 481abe9e59ca8..caeacfde2d5d8 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types @@ -159,22 +159,22 @@ if (obj5 instanceof C) { // narrowed to C1|C2. obj5.foo; >obj5.foo : string ->obj5 : C1 +>obj5 : C1 | C2 >foo : string obj5.c; >obj5.c : string ->obj5 : C1 +>obj5 : C1 | C2 >c : string obj5.bar1; ->obj5.bar1 : number ->obj5 : C1 ->bar1 : number +>obj5.bar1 : any +>obj5 : C1 | C2 +>bar1 : any obj5.bar2; >obj5.bar2 : any ->obj5 : C1 +>obj5 : C1 | C2 >bar2 : any } @@ -279,17 +279,17 @@ if (obj9 instanceof E) { // narrowed to E1 | E2 obj9.foo; >obj9.foo : string ->obj9 : E1 +>obj9 : E1 | E2 >foo : string obj9.bar1; ->obj9.bar1 : number ->obj9 : E1 ->bar1 : number +>obj9.bar1 : any +>obj9 : E1 | E2 +>bar1 : any obj9.bar2; >obj9.bar2 : any ->obj9 : E1 +>obj9 : E1 | E2 >bar2 : any } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsUnionWithUnion.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsUnionWithUnion.ts new file mode 100644 index 0000000000000..b071b87232f73 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsUnionWithUnion.ts @@ -0,0 +1,50 @@ +// @strictNullChecks: true +declare let a: string | undefined; + +declare function isEmptyStringOrUndefined(a: string | undefined): a is "" | undefined; +if (isEmptyStringOrUndefined(a)) { + a; // "" | undefined +} + +declare function isEmptyStringOrFoo(a: any): a is "" | "foo"; +if (isEmptyStringOrFoo(a)) { + a; // "" | "foo" +} + +declare function isNumberOrBoolean(a: any): a is number | boolean; +if (isNumberOrBoolean(a)) { + a; // never +} + +declare let b: "" | undefined; + +declare function isStringOrUndefined(b: any): b is string | undefined; +if (isStringOrUndefined(b)) { + b; // "" | undefined +} + +if (isNumberOrBoolean(b)) { + b; // never +} + +type A = { a: unknown }; +type B = { b: unknown }; +declare let c: { a: string } | { z: number }; + +declare function isAorB(c: any): c is A | B; +if (isAorB(c)) { + c; // { a: string } +} + +declare let d: A | B; + +declare function hasStringPropertyAOrIsBOrUndefined(d: any): d is { a: string } | B | undefined; +if (hasStringPropertyAOrIsBOrUndefined(d)) { + d; // { a: string } | B +} + +declare let e: { x: number } | { y: string }; +declare function isEightOrString(n: any): n is { x: 8 } | { x: string } +if (isEightOrString(e)) { + e; +}