mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Improve narrowing logic for instanceof, type predicate functions, and assertion functions (#49625)
* Improve narrowing logic for instanceof, type predicates, and assertions * Accept new baselines * Add tests * Tweak algorithm * Accept new baselines * Optimize for discriminated unions
This commit is contained in:
parent
4f29633934
commit
2c68ded954
@ -25405,23 +25405,32 @@ namespace ts {
|
||||
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)) {
|
||||
return assignableType;
|
||||
}
|
||||
if (type.flags & TypeFlags.AnyOrUnknown) {
|
||||
return candidate;
|
||||
}
|
||||
// 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.
|
||||
return isTypeSubtypeOf(candidate, type) ? candidate :
|
||||
isTypeAssignableTo(type, candidate) ? type :
|
||||
isTypeAssignableTo(candidate, type) ? candidate :
|
||||
getIntersectionType([type, candidate]);
|
||||
// We first attempt to filter the current type, narrowing constituents as appropriate and removing
|
||||
// constituents that are unrelated to the candidate.
|
||||
const keyPropertyName = type.flags & TypeFlags.Union ? getKeyPropertyName(type as UnionType) : undefined;
|
||||
const narrowedType = mapType(candidate, c => {
|
||||
// If a discriminant property is available, use that to reduce the type.
|
||||
const discriminant = keyPropertyName && getTypeOfPropertyOfType(c, keyPropertyName);
|
||||
const matching = discriminant && getConstituentTypeForKeyType(type as UnionType, discriminant);
|
||||
// For each constituent t in the current type, if t and and c are directly related, pick the most
|
||||
// specific of the two.
|
||||
const directlyRelated = mapType(matching || type, t => isRelated(t, c) ? t : isRelated(c, t) ? c : neverType);
|
||||
// If no constituents are directly related, create intersections for any generic constituents that
|
||||
// are related by constraint.
|
||||
return directlyRelated.flags & TypeFlags.Never ?
|
||||
mapType(type, t => maybeTypeOfKind(t, TypeFlags.Instantiable) && isRelated(c, getBaseConstraintOfType(t) || unknownType) ? getIntersectionType([t, c]) : neverType) :
|
||||
directlyRelated;
|
||||
});
|
||||
// If filtering produced a non-empty type, return that. Otherwise, pick the most specific of the two
|
||||
// based on assignability, or as a last resort produce an intersection.
|
||||
return !(narrowedType.flags & TypeFlags.Never) ? narrowedType :
|
||||
isTypeSubtypeOf(candidate, type) ? candidate :
|
||||
isTypeAssignableTo(type, candidate) ? type :
|
||||
isTypeAssignableTo(candidate, type) ? candidate :
|
||||
getIntersectionType([type, candidate]);
|
||||
}
|
||||
|
||||
function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
|
||||
|
||||
@ -1768,9 +1768,9 @@ function f30(o: Thing | undefined) {
|
||||
>foo : string | number | undefined
|
||||
|
||||
o.foo;
|
||||
>o.foo : string | number
|
||||
>o.foo : NonNullable<string | number | undefined>
|
||||
>o : Thing
|
||||
>foo : string | number
|
||||
>foo : NonNullable<string | number | undefined>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
423
tests/baselines/reference/narrowingUnionToUnion.js
Normal file
423
tests/baselines/reference/narrowingUnionToUnion.js
Normal file
@ -0,0 +1,423 @@
|
||||
//// [narrowingUnionToUnion.ts]
|
||||
type Falsy = false | 0 | 0n | '' | null | undefined;
|
||||
|
||||
declare function isFalsy(value: unknown): value is Falsy;
|
||||
|
||||
function fx1(x: string | number | undefined) {
|
||||
if (isFalsy(x)) {
|
||||
x; // "" | 0 | undefined
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T>(x: T | undefined) {
|
||||
if (isFalsy(x)) {
|
||||
x; // T & Falsy | undefined
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends string | number>(x: T) {
|
||||
if (isFalsy(x)) {
|
||||
x; // T & "" | T & 0
|
||||
}
|
||||
}
|
||||
|
||||
declare function isA(obj: unknown): obj is { a: false } | { b: 0 };
|
||||
|
||||
function fx4(obj: { b: number }) {
|
||||
if (isA(obj)) {
|
||||
obj; // { b: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
declare class X { x: string }
|
||||
declare class XS extends X { xs: string }
|
||||
|
||||
declare class Y { y: string }
|
||||
declare class YS extends Y { ys: string }
|
||||
|
||||
declare function isXSorY(obj: unknown): obj is XS | Y;
|
||||
|
||||
function fx5<T extends X>(obj: X | YS, c: typeof XS | typeof Y) {
|
||||
if (obj instanceof c) {
|
||||
obj; // XS | YS
|
||||
}
|
||||
if (isXSorY(obj)) {
|
||||
obj; // XS | YS
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #31156
|
||||
|
||||
declare function isEmptyStrOrUndefined(mixed: any): mixed is "" | undefined;
|
||||
|
||||
function fx10(s: string | undefined) {
|
||||
if (isEmptyStrOrUndefined(s)) {
|
||||
s; // "" | undefined
|
||||
if (s == undefined) {
|
||||
s; // undefined
|
||||
}
|
||||
else {
|
||||
s; // ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #37807
|
||||
|
||||
function f1(x: any): asserts x is number | undefined { }
|
||||
let v1: number | string | undefined;
|
||||
f1(v1);
|
||||
v1; // number | undefined
|
||||
|
||||
function f2(x: any): asserts x is 6 | undefined { }
|
||||
let v2: number | string | undefined;
|
||||
f2(v2);
|
||||
v2; // 6 | undefined
|
||||
|
||||
// #39105
|
||||
|
||||
declare function isEmptyString(value: string): value is '';
|
||||
declare function isMaybeEmptyString(value: string | null | undefined): value is '' | null | undefined;
|
||||
|
||||
declare function isZero(value: number): value is 0;
|
||||
declare function isMaybeZero(value: number | null | undefined): value is 0 | null | undefined;
|
||||
|
||||
declare function isEmptyArray<T>(value: T[]): value is [];
|
||||
declare function isMaybeEmptyArray<T>(value: T[] | null | undefined): value is [] | null | undefined;
|
||||
|
||||
const TEST_CASES = [
|
||||
(value: string) => {
|
||||
if (isEmptyString(value)) {
|
||||
value; // ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
if (isMaybeEmptyString(value)) {
|
||||
value; // ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
},
|
||||
(value?: string) => {
|
||||
if (isMaybeEmptyString(value)) {
|
||||
value; // "" | undefined
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
},
|
||||
(value: number) => {
|
||||
if (isZero(value)) {
|
||||
value; // 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
if (isMaybeZero(value)) {
|
||||
value; // 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
},
|
||||
(value?: number) => {
|
||||
if (isMaybeZero(value)) {
|
||||
value; // 0 | undefined
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
},
|
||||
(value: string[]) => {
|
||||
if (isEmptyArray(value)) {
|
||||
value; // []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
value; // []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
},
|
||||
(value?: string[]) => {
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
value; // [] | undefined
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
// Repro from #42101
|
||||
|
||||
type EmptyString = '' | null | undefined;
|
||||
|
||||
function isEmpty(value: string | EmptyString): value is EmptyString {
|
||||
return value === '' || value === null || value === undefined;
|
||||
}
|
||||
|
||||
let test: string | null | undefined;
|
||||
|
||||
if (isEmpty(test)) {
|
||||
test; // EmptyString
|
||||
}
|
||||
|
||||
// Repro from #43825
|
||||
|
||||
declare function assert<T>(value: any): asserts value is T
|
||||
|
||||
function test1(foo: number | string | boolean) {
|
||||
assert<1 | string>(foo);
|
||||
foo; // string | 1
|
||||
}
|
||||
|
||||
// Repro from #46909
|
||||
|
||||
function check1(x: unknown): x is (string | 0) {
|
||||
return typeof x === "string" || x === 0;
|
||||
}
|
||||
|
||||
function check2(x: unknown): x is ("hello" | 0) {
|
||||
return x === "hello" || x === 0;
|
||||
}
|
||||
|
||||
function test3(x: unknown) {
|
||||
if (typeof x === "string" || x === 0) {
|
||||
x; // string | 0
|
||||
if (x === "hello" || x === 0) {
|
||||
x; // 0 | "hello"
|
||||
}
|
||||
}
|
||||
if (check1(x)) {
|
||||
x; // string | 0
|
||||
if (check2(x)) {
|
||||
x; // 0 | "hello"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #49588
|
||||
|
||||
function assertRelationIsNullOrStringArray(v: (string | number)[] | null): asserts v is string[] | null {}
|
||||
|
||||
function f1x(obj: (string | number)[] | null) {
|
||||
assertRelationIsNullOrStringArray(obj);
|
||||
obj; // string[] | null
|
||||
}
|
||||
|
||||
|
||||
//// [narrowingUnionToUnion.js]
|
||||
"use strict";
|
||||
function fx1(x) {
|
||||
if (isFalsy(x)) {
|
||||
x; // "" | 0 | undefined
|
||||
}
|
||||
}
|
||||
function fx2(x) {
|
||||
if (isFalsy(x)) {
|
||||
x; // T & Falsy | undefined
|
||||
}
|
||||
}
|
||||
function fx3(x) {
|
||||
if (isFalsy(x)) {
|
||||
x; // T & "" | T & 0
|
||||
}
|
||||
}
|
||||
function fx4(obj) {
|
||||
if (isA(obj)) {
|
||||
obj; // { b: 0 }
|
||||
}
|
||||
}
|
||||
function fx5(obj, c) {
|
||||
if (obj instanceof c) {
|
||||
obj; // XS | YS
|
||||
}
|
||||
if (isXSorY(obj)) {
|
||||
obj; // XS | YS
|
||||
}
|
||||
}
|
||||
function fx10(s) {
|
||||
if (isEmptyStrOrUndefined(s)) {
|
||||
s; // "" | undefined
|
||||
if (s == undefined) {
|
||||
s; // undefined
|
||||
}
|
||||
else {
|
||||
s; // ""
|
||||
}
|
||||
}
|
||||
}
|
||||
// Repro from #37807
|
||||
function f1(x) { }
|
||||
var v1;
|
||||
f1(v1);
|
||||
v1; // number | undefined
|
||||
function f2(x) { }
|
||||
var v2;
|
||||
f2(v2);
|
||||
v2; // 6 | undefined
|
||||
var TEST_CASES = [
|
||||
function (value) {
|
||||
if (isEmptyString(value)) {
|
||||
value; // ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
if (isMaybeEmptyString(value)) {
|
||||
value; // ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
},
|
||||
function (value) {
|
||||
if (isMaybeEmptyString(value)) {
|
||||
value; // "" | undefined
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
},
|
||||
function (value) {
|
||||
if (isZero(value)) {
|
||||
value; // 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
if (isMaybeZero(value)) {
|
||||
value; // 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
},
|
||||
function (value) {
|
||||
if (isMaybeZero(value)) {
|
||||
value; // 0 | undefined
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
},
|
||||
function (value) {
|
||||
if (isEmptyArray(value)) {
|
||||
value; // []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
value; // []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
},
|
||||
function (value) {
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
value; // [] | undefined
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
},
|
||||
];
|
||||
function isEmpty(value) {
|
||||
return value === '' || value === null || value === undefined;
|
||||
}
|
||||
var test;
|
||||
if (isEmpty(test)) {
|
||||
test; // EmptyString
|
||||
}
|
||||
function test1(foo) {
|
||||
assert(foo);
|
||||
foo; // string | 1
|
||||
}
|
||||
// Repro from #46909
|
||||
function check1(x) {
|
||||
return typeof x === "string" || x === 0;
|
||||
}
|
||||
function check2(x) {
|
||||
return x === "hello" || x === 0;
|
||||
}
|
||||
function test3(x) {
|
||||
if (typeof x === "string" || x === 0) {
|
||||
x; // string | 0
|
||||
if (x === "hello" || x === 0) {
|
||||
x; // 0 | "hello"
|
||||
}
|
||||
}
|
||||
if (check1(x)) {
|
||||
x; // string | 0
|
||||
if (check2(x)) {
|
||||
x; // 0 | "hello"
|
||||
}
|
||||
}
|
||||
}
|
||||
// Repro from #49588
|
||||
function assertRelationIsNullOrStringArray(v) { }
|
||||
function f1x(obj) {
|
||||
assertRelationIsNullOrStringArray(obj);
|
||||
obj; // string[] | null
|
||||
}
|
||||
|
||||
|
||||
//// [narrowingUnionToUnion.d.ts]
|
||||
declare type Falsy = false | 0 | 0n | '' | null | undefined;
|
||||
declare function isFalsy(value: unknown): value is Falsy;
|
||||
declare function fx1(x: string | number | undefined): void;
|
||||
declare function fx2<T>(x: T | undefined): void;
|
||||
declare function fx3<T extends string | number>(x: T): void;
|
||||
declare function isA(obj: unknown): obj is {
|
||||
a: false;
|
||||
} | {
|
||||
b: 0;
|
||||
};
|
||||
declare function fx4(obj: {
|
||||
b: number;
|
||||
}): void;
|
||||
declare class X {
|
||||
x: string;
|
||||
}
|
||||
declare class XS extends X {
|
||||
xs: string;
|
||||
}
|
||||
declare class Y {
|
||||
y: string;
|
||||
}
|
||||
declare class YS extends Y {
|
||||
ys: string;
|
||||
}
|
||||
declare function isXSorY(obj: unknown): obj is XS | Y;
|
||||
declare function fx5<T extends X>(obj: X | YS, c: typeof XS | typeof Y): void;
|
||||
declare function isEmptyStrOrUndefined(mixed: any): mixed is "" | undefined;
|
||||
declare function fx10(s: string | undefined): void;
|
||||
declare function f1(x: any): asserts x is number | undefined;
|
||||
declare let v1: number | string | undefined;
|
||||
declare function f2(x: any): asserts x is 6 | undefined;
|
||||
declare let v2: number | string | undefined;
|
||||
declare function isEmptyString(value: string): value is '';
|
||||
declare function isMaybeEmptyString(value: string | null | undefined): value is '' | null | undefined;
|
||||
declare function isZero(value: number): value is 0;
|
||||
declare function isMaybeZero(value: number | null | undefined): value is 0 | null | undefined;
|
||||
declare function isEmptyArray<T>(value: T[]): value is [];
|
||||
declare function isMaybeEmptyArray<T>(value: T[] | null | undefined): value is [] | null | undefined;
|
||||
declare const TEST_CASES: (((value: string) => void) | ((value: number) => void) | ((value: string[]) => void))[];
|
||||
declare type EmptyString = '' | null | undefined;
|
||||
declare function isEmpty(value: string | EmptyString): value is EmptyString;
|
||||
declare let test: string | null | undefined;
|
||||
declare function assert<T>(value: any): asserts value is T;
|
||||
declare function test1(foo: number | string | boolean): void;
|
||||
declare function check1(x: unknown): x is (string | 0);
|
||||
declare function check2(x: unknown): x is ("hello" | 0);
|
||||
declare function test3(x: unknown): void;
|
||||
declare function assertRelationIsNullOrStringArray(v: (string | number)[] | null): asserts v is string[] | null;
|
||||
declare function f1x(obj: (string | number)[] | null): void;
|
||||
482
tests/baselines/reference/narrowingUnionToUnion.symbols
Normal file
482
tests/baselines/reference/narrowingUnionToUnion.symbols
Normal file
@ -0,0 +1,482 @@
|
||||
=== tests/cases/compiler/narrowingUnionToUnion.ts ===
|
||||
type Falsy = false | 0 | 0n | '' | null | undefined;
|
||||
>Falsy : Symbol(Falsy, Decl(narrowingUnionToUnion.ts, 0, 0))
|
||||
|
||||
declare function isFalsy(value: unknown): value is Falsy;
|
||||
>isFalsy : Symbol(isFalsy, Decl(narrowingUnionToUnion.ts, 0, 52))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 2, 25))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 2, 25))
|
||||
>Falsy : Symbol(Falsy, Decl(narrowingUnionToUnion.ts, 0, 0))
|
||||
|
||||
function fx1(x: string | number | undefined) {
|
||||
>fx1 : Symbol(fx1, Decl(narrowingUnionToUnion.ts, 2, 57))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 4, 13))
|
||||
|
||||
if (isFalsy(x)) {
|
||||
>isFalsy : Symbol(isFalsy, Decl(narrowingUnionToUnion.ts, 0, 52))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 4, 13))
|
||||
|
||||
x; // "" | 0 | undefined
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 4, 13))
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T>(x: T | undefined) {
|
||||
>fx2 : Symbol(fx2, Decl(narrowingUnionToUnion.ts, 8, 1))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 10, 13))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 10, 16))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 10, 13))
|
||||
|
||||
if (isFalsy(x)) {
|
||||
>isFalsy : Symbol(isFalsy, Decl(narrowingUnionToUnion.ts, 0, 52))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 10, 16))
|
||||
|
||||
x; // T & Falsy | undefined
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 10, 16))
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends string | number>(x: T) {
|
||||
>fx3 : Symbol(fx3, Decl(narrowingUnionToUnion.ts, 14, 1))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 16, 13))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 16, 40))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 16, 13))
|
||||
|
||||
if (isFalsy(x)) {
|
||||
>isFalsy : Symbol(isFalsy, Decl(narrowingUnionToUnion.ts, 0, 52))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 16, 40))
|
||||
|
||||
x; // T & "" | T & 0
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 16, 40))
|
||||
}
|
||||
}
|
||||
|
||||
declare function isA(obj: unknown): obj is { a: false } | { b: 0 };
|
||||
>isA : Symbol(isA, Decl(narrowingUnionToUnion.ts, 20, 1))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 22, 21))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 22, 21))
|
||||
>a : Symbol(a, Decl(narrowingUnionToUnion.ts, 22, 44))
|
||||
>b : Symbol(b, Decl(narrowingUnionToUnion.ts, 22, 59))
|
||||
|
||||
function fx4(obj: { b: number }) {
|
||||
>fx4 : Symbol(fx4, Decl(narrowingUnionToUnion.ts, 22, 67))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 24, 13))
|
||||
>b : Symbol(b, Decl(narrowingUnionToUnion.ts, 24, 19))
|
||||
|
||||
if (isA(obj)) {
|
||||
>isA : Symbol(isA, Decl(narrowingUnionToUnion.ts, 20, 1))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 24, 13))
|
||||
|
||||
obj; // { b: 0 }
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 24, 13))
|
||||
}
|
||||
}
|
||||
|
||||
declare class X { x: string }
|
||||
>X : Symbol(X, Decl(narrowingUnionToUnion.ts, 28, 1))
|
||||
>x : Symbol(X.x, Decl(narrowingUnionToUnion.ts, 30, 17))
|
||||
|
||||
declare class XS extends X { xs: string }
|
||||
>XS : Symbol(XS, Decl(narrowingUnionToUnion.ts, 30, 29))
|
||||
>X : Symbol(X, Decl(narrowingUnionToUnion.ts, 28, 1))
|
||||
>xs : Symbol(XS.xs, Decl(narrowingUnionToUnion.ts, 31, 28))
|
||||
|
||||
declare class Y { y: string }
|
||||
>Y : Symbol(Y, Decl(narrowingUnionToUnion.ts, 31, 41))
|
||||
>y : Symbol(Y.y, Decl(narrowingUnionToUnion.ts, 33, 17))
|
||||
|
||||
declare class YS extends Y { ys: string }
|
||||
>YS : Symbol(YS, Decl(narrowingUnionToUnion.ts, 33, 29))
|
||||
>Y : Symbol(Y, Decl(narrowingUnionToUnion.ts, 31, 41))
|
||||
>ys : Symbol(YS.ys, Decl(narrowingUnionToUnion.ts, 34, 28))
|
||||
|
||||
declare function isXSorY(obj: unknown): obj is XS | Y;
|
||||
>isXSorY : Symbol(isXSorY, Decl(narrowingUnionToUnion.ts, 34, 41))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 36, 25))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 36, 25))
|
||||
>XS : Symbol(XS, Decl(narrowingUnionToUnion.ts, 30, 29))
|
||||
>Y : Symbol(Y, Decl(narrowingUnionToUnion.ts, 31, 41))
|
||||
|
||||
function fx5<T extends X>(obj: X | YS, c: typeof XS | typeof Y) {
|
||||
>fx5 : Symbol(fx5, Decl(narrowingUnionToUnion.ts, 36, 54))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 38, 13))
|
||||
>X : Symbol(X, Decl(narrowingUnionToUnion.ts, 28, 1))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 38, 26))
|
||||
>X : Symbol(X, Decl(narrowingUnionToUnion.ts, 28, 1))
|
||||
>YS : Symbol(YS, Decl(narrowingUnionToUnion.ts, 33, 29))
|
||||
>c : Symbol(c, Decl(narrowingUnionToUnion.ts, 38, 38))
|
||||
>XS : Symbol(XS, Decl(narrowingUnionToUnion.ts, 30, 29))
|
||||
>Y : Symbol(Y, Decl(narrowingUnionToUnion.ts, 31, 41))
|
||||
|
||||
if (obj instanceof c) {
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 38, 26))
|
||||
>c : Symbol(c, Decl(narrowingUnionToUnion.ts, 38, 38))
|
||||
|
||||
obj; // XS | YS
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 38, 26))
|
||||
}
|
||||
if (isXSorY(obj)) {
|
||||
>isXSorY : Symbol(isXSorY, Decl(narrowingUnionToUnion.ts, 34, 41))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 38, 26))
|
||||
|
||||
obj; // XS | YS
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 38, 26))
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #31156
|
||||
|
||||
declare function isEmptyStrOrUndefined(mixed: any): mixed is "" | undefined;
|
||||
>isEmptyStrOrUndefined : Symbol(isEmptyStrOrUndefined, Decl(narrowingUnionToUnion.ts, 45, 1))
|
||||
>mixed : Symbol(mixed, Decl(narrowingUnionToUnion.ts, 49, 39))
|
||||
>mixed : Symbol(mixed, Decl(narrowingUnionToUnion.ts, 49, 39))
|
||||
|
||||
function fx10(s: string | undefined) {
|
||||
>fx10 : Symbol(fx10, Decl(narrowingUnionToUnion.ts, 49, 76))
|
||||
>s : Symbol(s, Decl(narrowingUnionToUnion.ts, 51, 14))
|
||||
|
||||
if (isEmptyStrOrUndefined(s)) {
|
||||
>isEmptyStrOrUndefined : Symbol(isEmptyStrOrUndefined, Decl(narrowingUnionToUnion.ts, 45, 1))
|
||||
>s : Symbol(s, Decl(narrowingUnionToUnion.ts, 51, 14))
|
||||
|
||||
s; // "" | undefined
|
||||
>s : Symbol(s, Decl(narrowingUnionToUnion.ts, 51, 14))
|
||||
|
||||
if (s == undefined) {
|
||||
>s : Symbol(s, Decl(narrowingUnionToUnion.ts, 51, 14))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
s; // undefined
|
||||
>s : Symbol(s, Decl(narrowingUnionToUnion.ts, 51, 14))
|
||||
}
|
||||
else {
|
||||
s; // ""
|
||||
>s : Symbol(s, Decl(narrowingUnionToUnion.ts, 51, 14))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #37807
|
||||
|
||||
function f1(x: any): asserts x is number | undefined { }
|
||||
>f1 : Symbol(f1, Decl(narrowingUnionToUnion.ts, 61, 1))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 65, 12))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 65, 12))
|
||||
|
||||
let v1: number | string | undefined;
|
||||
>v1 : Symbol(v1, Decl(narrowingUnionToUnion.ts, 66, 3))
|
||||
|
||||
f1(v1);
|
||||
>f1 : Symbol(f1, Decl(narrowingUnionToUnion.ts, 61, 1))
|
||||
>v1 : Symbol(v1, Decl(narrowingUnionToUnion.ts, 66, 3))
|
||||
|
||||
v1; // number | undefined
|
||||
>v1 : Symbol(v1, Decl(narrowingUnionToUnion.ts, 66, 3))
|
||||
|
||||
function f2(x: any): asserts x is 6 | undefined { }
|
||||
>f2 : Symbol(f2, Decl(narrowingUnionToUnion.ts, 68, 3))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 70, 12))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 70, 12))
|
||||
|
||||
let v2: number | string | undefined;
|
||||
>v2 : Symbol(v2, Decl(narrowingUnionToUnion.ts, 71, 3))
|
||||
|
||||
f2(v2);
|
||||
>f2 : Symbol(f2, Decl(narrowingUnionToUnion.ts, 68, 3))
|
||||
>v2 : Symbol(v2, Decl(narrowingUnionToUnion.ts, 71, 3))
|
||||
|
||||
v2; // 6 | undefined
|
||||
>v2 : Symbol(v2, Decl(narrowingUnionToUnion.ts, 71, 3))
|
||||
|
||||
// #39105
|
||||
|
||||
declare function isEmptyString(value: string): value is '';
|
||||
>isEmptyString : Symbol(isEmptyString, Decl(narrowingUnionToUnion.ts, 73, 3))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 77, 31))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 77, 31))
|
||||
|
||||
declare function isMaybeEmptyString(value: string | null | undefined): value is '' | null | undefined;
|
||||
>isMaybeEmptyString : Symbol(isMaybeEmptyString, Decl(narrowingUnionToUnion.ts, 77, 59))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 78, 36))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 78, 36))
|
||||
|
||||
declare function isZero(value: number): value is 0;
|
||||
>isZero : Symbol(isZero, Decl(narrowingUnionToUnion.ts, 78, 102))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 80, 24))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 80, 24))
|
||||
|
||||
declare function isMaybeZero(value: number | null | undefined): value is 0 | null | undefined;
|
||||
>isMaybeZero : Symbol(isMaybeZero, Decl(narrowingUnionToUnion.ts, 80, 51))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 81, 29))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 81, 29))
|
||||
|
||||
declare function isEmptyArray<T>(value: T[]): value is [];
|
||||
>isEmptyArray : Symbol(isEmptyArray, Decl(narrowingUnionToUnion.ts, 81, 94))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 83, 30))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 83, 33))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 83, 30))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 83, 33))
|
||||
|
||||
declare function isMaybeEmptyArray<T>(value: T[] | null | undefined): value is [] | null | undefined;
|
||||
>isMaybeEmptyArray : Symbol(isMaybeEmptyArray, Decl(narrowingUnionToUnion.ts, 83, 58))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 84, 35))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 84, 38))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 84, 35))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 84, 38))
|
||||
|
||||
const TEST_CASES = [
|
||||
>TEST_CASES : Symbol(TEST_CASES, Decl(narrowingUnionToUnion.ts, 86, 5))
|
||||
|
||||
(value: string) => {
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 87, 5))
|
||||
|
||||
if (isEmptyString(value)) {
|
||||
>isEmptyString : Symbol(isEmptyString, Decl(narrowingUnionToUnion.ts, 73, 3))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 87, 5))
|
||||
|
||||
value; // ""
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 87, 5))
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 87, 5))
|
||||
}
|
||||
if (isMaybeEmptyString(value)) {
|
||||
>isMaybeEmptyString : Symbol(isMaybeEmptyString, Decl(narrowingUnionToUnion.ts, 77, 59))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 87, 5))
|
||||
|
||||
value; // ""
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 87, 5))
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 87, 5))
|
||||
}
|
||||
},
|
||||
(value?: string) => {
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 101, 5))
|
||||
|
||||
if (isMaybeEmptyString(value)) {
|
||||
>isMaybeEmptyString : Symbol(isMaybeEmptyString, Decl(narrowingUnionToUnion.ts, 77, 59))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 101, 5))
|
||||
|
||||
value; // "" | undefined
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 101, 5))
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 101, 5))
|
||||
}
|
||||
},
|
||||
(value: number) => {
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 109, 5))
|
||||
|
||||
if (isZero(value)) {
|
||||
>isZero : Symbol(isZero, Decl(narrowingUnionToUnion.ts, 78, 102))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 109, 5))
|
||||
|
||||
value; // 0
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 109, 5))
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 109, 5))
|
||||
}
|
||||
if (isMaybeZero(value)) {
|
||||
>isMaybeZero : Symbol(isMaybeZero, Decl(narrowingUnionToUnion.ts, 80, 51))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 109, 5))
|
||||
|
||||
value; // 0
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 109, 5))
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 109, 5))
|
||||
}
|
||||
},
|
||||
(value?: number) => {
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 123, 5))
|
||||
|
||||
if (isMaybeZero(value)) {
|
||||
>isMaybeZero : Symbol(isMaybeZero, Decl(narrowingUnionToUnion.ts, 80, 51))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 123, 5))
|
||||
|
||||
value; // 0 | undefined
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 123, 5))
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 123, 5))
|
||||
}
|
||||
},
|
||||
(value: string[]) => {
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 131, 5))
|
||||
|
||||
if (isEmptyArray(value)) {
|
||||
>isEmptyArray : Symbol(isEmptyArray, Decl(narrowingUnionToUnion.ts, 81, 94))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 131, 5))
|
||||
|
||||
value; // []
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 131, 5))
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 131, 5))
|
||||
}
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
>isMaybeEmptyArray : Symbol(isMaybeEmptyArray, Decl(narrowingUnionToUnion.ts, 83, 58))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 131, 5))
|
||||
|
||||
value; // []
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 131, 5))
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 131, 5))
|
||||
}
|
||||
},
|
||||
(value?: string[]) => {
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 145, 5))
|
||||
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
>isMaybeEmptyArray : Symbol(isMaybeEmptyArray, Decl(narrowingUnionToUnion.ts, 83, 58))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 145, 5))
|
||||
|
||||
value; // [] | undefined
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 145, 5))
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 145, 5))
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
// Repro from #42101
|
||||
|
||||
type EmptyString = '' | null | undefined;
|
||||
>EmptyString : Symbol(EmptyString, Decl(narrowingUnionToUnion.ts, 153, 2))
|
||||
|
||||
function isEmpty(value: string | EmptyString): value is EmptyString {
|
||||
>isEmpty : Symbol(isEmpty, Decl(narrowingUnionToUnion.ts, 157, 41))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 159, 17))
|
||||
>EmptyString : Symbol(EmptyString, Decl(narrowingUnionToUnion.ts, 153, 2))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 159, 17))
|
||||
>EmptyString : Symbol(EmptyString, Decl(narrowingUnionToUnion.ts, 153, 2))
|
||||
|
||||
return value === '' || value === null || value === undefined;
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 159, 17))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 159, 17))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 159, 17))
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
let test: string | null | undefined;
|
||||
>test : Symbol(test, Decl(narrowingUnionToUnion.ts, 163, 3))
|
||||
|
||||
if (isEmpty(test)) {
|
||||
>isEmpty : Symbol(isEmpty, Decl(narrowingUnionToUnion.ts, 157, 41))
|
||||
>test : Symbol(test, Decl(narrowingUnionToUnion.ts, 163, 3))
|
||||
|
||||
test; // EmptyString
|
||||
>test : Symbol(test, Decl(narrowingUnionToUnion.ts, 163, 3))
|
||||
}
|
||||
|
||||
// Repro from #43825
|
||||
|
||||
declare function assert<T>(value: any): asserts value is T
|
||||
>assert : Symbol(assert, Decl(narrowingUnionToUnion.ts, 167, 1))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 171, 24))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 171, 27))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 171, 27))
|
||||
>T : Symbol(T, Decl(narrowingUnionToUnion.ts, 171, 24))
|
||||
|
||||
function test1(foo: number | string | boolean) {
|
||||
>test1 : Symbol(test1, Decl(narrowingUnionToUnion.ts, 171, 58))
|
||||
>foo : Symbol(foo, Decl(narrowingUnionToUnion.ts, 173, 15))
|
||||
|
||||
assert<1 | string>(foo);
|
||||
>assert : Symbol(assert, Decl(narrowingUnionToUnion.ts, 167, 1))
|
||||
>foo : Symbol(foo, Decl(narrowingUnionToUnion.ts, 173, 15))
|
||||
|
||||
foo; // string | 1
|
||||
>foo : Symbol(foo, Decl(narrowingUnionToUnion.ts, 173, 15))
|
||||
}
|
||||
|
||||
// Repro from #46909
|
||||
|
||||
function check1(x: unknown): x is (string | 0) {
|
||||
>check1 : Symbol(check1, Decl(narrowingUnionToUnion.ts, 176, 1))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 180, 16))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 180, 16))
|
||||
|
||||
return typeof x === "string" || x === 0;
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 180, 16))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 180, 16))
|
||||
}
|
||||
|
||||
function check2(x: unknown): x is ("hello" | 0) {
|
||||
>check2 : Symbol(check2, Decl(narrowingUnionToUnion.ts, 182, 1))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 184, 16))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 184, 16))
|
||||
|
||||
return x === "hello" || x === 0;
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 184, 16))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 184, 16))
|
||||
}
|
||||
|
||||
function test3(x: unknown) {
|
||||
>test3 : Symbol(test3, Decl(narrowingUnionToUnion.ts, 186, 1))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
|
||||
if (typeof x === "string" || x === 0) {
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
|
||||
x; // string | 0
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
|
||||
if (x === "hello" || x === 0) {
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
|
||||
x; // 0 | "hello"
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
}
|
||||
}
|
||||
if (check1(x)) {
|
||||
>check1 : Symbol(check1, Decl(narrowingUnionToUnion.ts, 176, 1))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
|
||||
x; // string | 0
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
|
||||
if (check2(x)) {
|
||||
>check2 : Symbol(check2, Decl(narrowingUnionToUnion.ts, 182, 1))
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
|
||||
x; // 0 | "hello"
|
||||
>x : Symbol(x, Decl(narrowingUnionToUnion.ts, 188, 15))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #49588
|
||||
|
||||
function assertRelationIsNullOrStringArray(v: (string | number)[] | null): asserts v is string[] | null {}
|
||||
>assertRelationIsNullOrStringArray : Symbol(assertRelationIsNullOrStringArray, Decl(narrowingUnionToUnion.ts, 201, 1))
|
||||
>v : Symbol(v, Decl(narrowingUnionToUnion.ts, 205, 43))
|
||||
>v : Symbol(v, Decl(narrowingUnionToUnion.ts, 205, 43))
|
||||
|
||||
function f1x(obj: (string | number)[] | null) {
|
||||
>f1x : Symbol(f1x, Decl(narrowingUnionToUnion.ts, 205, 106))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 207, 13))
|
||||
|
||||
assertRelationIsNullOrStringArray(obj);
|
||||
>assertRelationIsNullOrStringArray : Symbol(assertRelationIsNullOrStringArray, Decl(narrowingUnionToUnion.ts, 201, 1))
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 207, 13))
|
||||
|
||||
obj; // string[] | null
|
||||
>obj : Symbol(obj, Decl(narrowingUnionToUnion.ts, 207, 13))
|
||||
}
|
||||
|
||||
520
tests/baselines/reference/narrowingUnionToUnion.types
Normal file
520
tests/baselines/reference/narrowingUnionToUnion.types
Normal file
@ -0,0 +1,520 @@
|
||||
=== tests/cases/compiler/narrowingUnionToUnion.ts ===
|
||||
type Falsy = false | 0 | 0n | '' | null | undefined;
|
||||
>Falsy : false | "" | 0 | 0n | null | undefined
|
||||
>false : false
|
||||
>null : null
|
||||
|
||||
declare function isFalsy(value: unknown): value is Falsy;
|
||||
>isFalsy : (value: unknown) => value is Falsy
|
||||
>value : unknown
|
||||
|
||||
function fx1(x: string | number | undefined) {
|
||||
>fx1 : (x: string | number | undefined) => void
|
||||
>x : string | number | undefined
|
||||
|
||||
if (isFalsy(x)) {
|
||||
>isFalsy(x) : boolean
|
||||
>isFalsy : (value: unknown) => value is Falsy
|
||||
>x : string | number | undefined
|
||||
|
||||
x; // "" | 0 | undefined
|
||||
>x : "" | 0 | undefined
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T>(x: T | undefined) {
|
||||
>fx2 : <T>(x: T | undefined) => void
|
||||
>x : T | undefined
|
||||
|
||||
if (isFalsy(x)) {
|
||||
>isFalsy(x) : boolean
|
||||
>isFalsy : (value: unknown) => value is Falsy
|
||||
>x : T | undefined
|
||||
|
||||
x; // T & Falsy | undefined
|
||||
>x : (T & null) | (T & false) | (T & "") | (T & 0) | (T & 0n) | undefined
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends string | number>(x: T) {
|
||||
>fx3 : <T extends string | number>(x: T) => void
|
||||
>x : T
|
||||
|
||||
if (isFalsy(x)) {
|
||||
>isFalsy(x) : boolean
|
||||
>isFalsy : (value: unknown) => value is Falsy
|
||||
>x : string | number
|
||||
|
||||
x; // T & "" | T & 0
|
||||
>x : (T & "") | (T & 0)
|
||||
}
|
||||
}
|
||||
|
||||
declare function isA(obj: unknown): obj is { a: false } | { b: 0 };
|
||||
>isA : (obj: unknown) => obj is { a: false; } | { b: 0; }
|
||||
>obj : unknown
|
||||
>a : false
|
||||
>false : false
|
||||
>b : 0
|
||||
|
||||
function fx4(obj: { b: number }) {
|
||||
>fx4 : (obj: { b: number;}) => void
|
||||
>obj : { b: number; }
|
||||
>b : number
|
||||
|
||||
if (isA(obj)) {
|
||||
>isA(obj) : boolean
|
||||
>isA : (obj: unknown) => obj is { a: false; } | { b: 0; }
|
||||
>obj : { b: number; }
|
||||
|
||||
obj; // { b: 0 }
|
||||
>obj : { b: 0; }
|
||||
}
|
||||
}
|
||||
|
||||
declare class X { x: string }
|
||||
>X : X
|
||||
>x : string
|
||||
|
||||
declare class XS extends X { xs: string }
|
||||
>XS : XS
|
||||
>X : X
|
||||
>xs : string
|
||||
|
||||
declare class Y { y: string }
|
||||
>Y : Y
|
||||
>y : string
|
||||
|
||||
declare class YS extends Y { ys: string }
|
||||
>YS : YS
|
||||
>Y : Y
|
||||
>ys : string
|
||||
|
||||
declare function isXSorY(obj: unknown): obj is XS | Y;
|
||||
>isXSorY : (obj: unknown) => obj is XS | Y
|
||||
>obj : unknown
|
||||
|
||||
function fx5<T extends X>(obj: X | YS, c: typeof XS | typeof Y) {
|
||||
>fx5 : <T extends X>(obj: X | YS, c: typeof XS | typeof Y) => void
|
||||
>obj : X | YS
|
||||
>c : typeof XS | typeof Y
|
||||
>XS : typeof XS
|
||||
>Y : typeof Y
|
||||
|
||||
if (obj instanceof c) {
|
||||
>obj instanceof c : boolean
|
||||
>obj : X | YS
|
||||
>c : typeof XS | typeof Y
|
||||
|
||||
obj; // XS | YS
|
||||
>obj : XS | YS
|
||||
}
|
||||
if (isXSorY(obj)) {
|
||||
>isXSorY(obj) : boolean
|
||||
>isXSorY : (obj: unknown) => obj is XS | Y
|
||||
>obj : X | YS
|
||||
|
||||
obj; // XS | YS
|
||||
>obj : XS | YS
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #31156
|
||||
|
||||
declare function isEmptyStrOrUndefined(mixed: any): mixed is "" | undefined;
|
||||
>isEmptyStrOrUndefined : (mixed: any) => mixed is "" | undefined
|
||||
>mixed : any
|
||||
|
||||
function fx10(s: string | undefined) {
|
||||
>fx10 : (s: string | undefined) => void
|
||||
>s : string | undefined
|
||||
|
||||
if (isEmptyStrOrUndefined(s)) {
|
||||
>isEmptyStrOrUndefined(s) : boolean
|
||||
>isEmptyStrOrUndefined : (mixed: any) => mixed is "" | undefined
|
||||
>s : string | undefined
|
||||
|
||||
s; // "" | undefined
|
||||
>s : "" | undefined
|
||||
|
||||
if (s == undefined) {
|
||||
>s == undefined : boolean
|
||||
>s : "" | undefined
|
||||
>undefined : undefined
|
||||
|
||||
s; // undefined
|
||||
>s : undefined
|
||||
}
|
||||
else {
|
||||
s; // ""
|
||||
>s : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #37807
|
||||
|
||||
function f1(x: any): asserts x is number | undefined { }
|
||||
>f1 : (x: any) => asserts x is number | undefined
|
||||
>x : any
|
||||
|
||||
let v1: number | string | undefined;
|
||||
>v1 : string | number | undefined
|
||||
|
||||
f1(v1);
|
||||
>f1(v1) : void
|
||||
>f1 : (x: any) => asserts x is number | undefined
|
||||
>v1 : string | number | undefined
|
||||
|
||||
v1; // number | undefined
|
||||
>v1 : number | undefined
|
||||
|
||||
function f2(x: any): asserts x is 6 | undefined { }
|
||||
>f2 : (x: any) => asserts x is 6 | undefined
|
||||
>x : any
|
||||
|
||||
let v2: number | string | undefined;
|
||||
>v2 : string | number | undefined
|
||||
|
||||
f2(v2);
|
||||
>f2(v2) : void
|
||||
>f2 : (x: any) => asserts x is 6 | undefined
|
||||
>v2 : string | number | undefined
|
||||
|
||||
v2; // 6 | undefined
|
||||
>v2 : 6 | undefined
|
||||
|
||||
// #39105
|
||||
|
||||
declare function isEmptyString(value: string): value is '';
|
||||
>isEmptyString : (value: string) => value is ""
|
||||
>value : string
|
||||
|
||||
declare function isMaybeEmptyString(value: string | null | undefined): value is '' | null | undefined;
|
||||
>isMaybeEmptyString : (value: string | null | undefined) => value is "" | null | undefined
|
||||
>value : string | null | undefined
|
||||
>null : null
|
||||
>null : null
|
||||
|
||||
declare function isZero(value: number): value is 0;
|
||||
>isZero : (value: number) => value is 0
|
||||
>value : number
|
||||
|
||||
declare function isMaybeZero(value: number | null | undefined): value is 0 | null | undefined;
|
||||
>isMaybeZero : (value: number | null | undefined) => value is 0 | null | undefined
|
||||
>value : number | null | undefined
|
||||
>null : null
|
||||
>null : null
|
||||
|
||||
declare function isEmptyArray<T>(value: T[]): value is [];
|
||||
>isEmptyArray : <T>(value: T[]) => value is []
|
||||
>value : T[]
|
||||
|
||||
declare function isMaybeEmptyArray<T>(value: T[] | null | undefined): value is [] | null | undefined;
|
||||
>isMaybeEmptyArray : <T>(value: T[] | null | undefined) => value is [] | null | undefined
|
||||
>value : T[] | null | undefined
|
||||
>null : null
|
||||
>null : null
|
||||
|
||||
const TEST_CASES = [
|
||||
>TEST_CASES : (((value: string) => void) | ((value: number) => void) | ((value: string[]) => void))[]
|
||||
>[ (value: string) => { if (isEmptyString(value)) { value; // "" } else { value; // string } if (isMaybeEmptyString(value)) { value; // "" } else { value; // string } }, (value?: string) => { if (isMaybeEmptyString(value)) { value; // "" | undefined } else { value; // string } }, (value: number) => { if (isZero(value)) { value; // 0 } else { value; // number } if (isMaybeZero(value)) { value; // 0 } else { value; // number } }, (value?: number) => { if (isMaybeZero(value)) { value; // 0 | undefined } else { value; // number } }, (value: string[]) => { if (isEmptyArray(value)) { value; // [] } else { value; // string[] } if (isMaybeEmptyArray(value)) { value; // [] } else { value; // string[] } }, (value?: string[]) => { if (isMaybeEmptyArray(value)) { value; // [] | undefined } else { value; // string[] } },] : (((value: string) => void) | ((value: number) => void) | ((value: string[]) => void))[]
|
||||
|
||||
(value: string) => {
|
||||
>(value: string) => { if (isEmptyString(value)) { value; // "" } else { value; // string } if (isMaybeEmptyString(value)) { value; // "" } else { value; // string } } : (value: string) => void
|
||||
>value : string
|
||||
|
||||
if (isEmptyString(value)) {
|
||||
>isEmptyString(value) : boolean
|
||||
>isEmptyString : (value: string) => value is ""
|
||||
>value : string
|
||||
|
||||
value; // ""
|
||||
>value : ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
>value : string
|
||||
}
|
||||
if (isMaybeEmptyString(value)) {
|
||||
>isMaybeEmptyString(value) : boolean
|
||||
>isMaybeEmptyString : (value: string | null | undefined) => value is "" | null | undefined
|
||||
>value : string
|
||||
|
||||
value; // ""
|
||||
>value : ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
>value : string
|
||||
}
|
||||
},
|
||||
(value?: string) => {
|
||||
>(value?: string) => { if (isMaybeEmptyString(value)) { value; // "" | undefined } else { value; // string } } : (value?: string) => void
|
||||
>value : string | undefined
|
||||
|
||||
if (isMaybeEmptyString(value)) {
|
||||
>isMaybeEmptyString(value) : boolean
|
||||
>isMaybeEmptyString : (value: string | null | undefined) => value is "" | null | undefined
|
||||
>value : string | undefined
|
||||
|
||||
value; // "" | undefined
|
||||
>value : "" | undefined
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
>value : string
|
||||
}
|
||||
},
|
||||
(value: number) => {
|
||||
>(value: number) => { if (isZero(value)) { value; // 0 } else { value; // number } if (isMaybeZero(value)) { value; // 0 } else { value; // number } } : (value: number) => void
|
||||
>value : number
|
||||
|
||||
if (isZero(value)) {
|
||||
>isZero(value) : boolean
|
||||
>isZero : (value: number) => value is 0
|
||||
>value : number
|
||||
|
||||
value; // 0
|
||||
>value : 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
>value : number
|
||||
}
|
||||
if (isMaybeZero(value)) {
|
||||
>isMaybeZero(value) : boolean
|
||||
>isMaybeZero : (value: number | null | undefined) => value is 0 | null | undefined
|
||||
>value : number
|
||||
|
||||
value; // 0
|
||||
>value : 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
>value : number
|
||||
}
|
||||
},
|
||||
(value?: number) => {
|
||||
>(value?: number) => { if (isMaybeZero(value)) { value; // 0 | undefined } else { value; // number } } : (value?: number) => void
|
||||
>value : number | undefined
|
||||
|
||||
if (isMaybeZero(value)) {
|
||||
>isMaybeZero(value) : boolean
|
||||
>isMaybeZero : (value: number | null | undefined) => value is 0 | null | undefined
|
||||
>value : number | undefined
|
||||
|
||||
value; // 0 | undefined
|
||||
>value : 0 | undefined
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
>value : number
|
||||
}
|
||||
},
|
||||
(value: string[]) => {
|
||||
>(value: string[]) => { if (isEmptyArray(value)) { value; // [] } else { value; // string[] } if (isMaybeEmptyArray(value)) { value; // [] } else { value; // string[] } } : (value: string[]) => void
|
||||
>value : string[]
|
||||
|
||||
if (isEmptyArray(value)) {
|
||||
>isEmptyArray(value) : boolean
|
||||
>isEmptyArray : <T>(value: T[]) => value is []
|
||||
>value : string[]
|
||||
|
||||
value; // []
|
||||
>value : []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
>value : string[]
|
||||
}
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
>isMaybeEmptyArray(value) : boolean
|
||||
>isMaybeEmptyArray : <T>(value: T[] | null | undefined) => value is [] | null | undefined
|
||||
>value : string[]
|
||||
|
||||
value; // []
|
||||
>value : []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
>value : string[]
|
||||
}
|
||||
},
|
||||
(value?: string[]) => {
|
||||
>(value?: string[]) => { if (isMaybeEmptyArray(value)) { value; // [] | undefined } else { value; // string[] } } : (value?: string[]) => void
|
||||
>value : string[] | undefined
|
||||
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
>isMaybeEmptyArray(value) : boolean
|
||||
>isMaybeEmptyArray : <T>(value: T[] | null | undefined) => value is [] | null | undefined
|
||||
>value : string[] | undefined
|
||||
|
||||
value; // [] | undefined
|
||||
>value : [] | undefined
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
>value : string[]
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
// Repro from #42101
|
||||
|
||||
type EmptyString = '' | null | undefined;
|
||||
>EmptyString : "" | null | undefined
|
||||
>null : null
|
||||
|
||||
function isEmpty(value: string | EmptyString): value is EmptyString {
|
||||
>isEmpty : (value: string | EmptyString) => value is EmptyString
|
||||
>value : string | null | undefined
|
||||
|
||||
return value === '' || value === null || value === undefined;
|
||||
>value === '' || value === null || value === undefined : boolean
|
||||
>value === '' || value === null : boolean
|
||||
>value === '' : boolean
|
||||
>value : string | null | undefined
|
||||
>'' : ""
|
||||
>value === null : boolean
|
||||
>value : string | null | undefined
|
||||
>null : null
|
||||
>value === undefined : boolean
|
||||
>value : string | undefined
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
let test: string | null | undefined;
|
||||
>test : string | null | undefined
|
||||
>null : null
|
||||
|
||||
if (isEmpty(test)) {
|
||||
>isEmpty(test) : boolean
|
||||
>isEmpty : (value: string | null | undefined) => value is EmptyString
|
||||
>test : string | null | undefined
|
||||
|
||||
test; // EmptyString
|
||||
>test : EmptyString
|
||||
}
|
||||
|
||||
// Repro from #43825
|
||||
|
||||
declare function assert<T>(value: any): asserts value is T
|
||||
>assert : <T>(value: any) => asserts value is T
|
||||
>value : any
|
||||
|
||||
function test1(foo: number | string | boolean) {
|
||||
>test1 : (foo: number | string | boolean) => void
|
||||
>foo : string | number | boolean
|
||||
|
||||
assert<1 | string>(foo);
|
||||
>assert<1 | string>(foo) : void
|
||||
>assert : <T>(value: any) => asserts value is T
|
||||
>foo : string | number | boolean
|
||||
|
||||
foo; // string | 1
|
||||
>foo : string | 1
|
||||
}
|
||||
|
||||
// Repro from #46909
|
||||
|
||||
function check1(x: unknown): x is (string | 0) {
|
||||
>check1 : (x: unknown) => x is string | 0
|
||||
>x : unknown
|
||||
|
||||
return typeof x === "string" || x === 0;
|
||||
>typeof x === "string" || x === 0 : boolean
|
||||
>typeof x === "string" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>"string" : "string"
|
||||
>x === 0 : boolean
|
||||
>x : unknown
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
function check2(x: unknown): x is ("hello" | 0) {
|
||||
>check2 : (x: unknown) => x is 0 | "hello"
|
||||
>x : unknown
|
||||
|
||||
return x === "hello" || x === 0;
|
||||
>x === "hello" || x === 0 : boolean
|
||||
>x === "hello" : boolean
|
||||
>x : unknown
|
||||
>"hello" : "hello"
|
||||
>x === 0 : boolean
|
||||
>x : unknown
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
function test3(x: unknown) {
|
||||
>test3 : (x: unknown) => void
|
||||
>x : unknown
|
||||
|
||||
if (typeof x === "string" || x === 0) {
|
||||
>typeof x === "string" || x === 0 : boolean
|
||||
>typeof x === "string" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>"string" : "string"
|
||||
>x === 0 : boolean
|
||||
>x : unknown
|
||||
>0 : 0
|
||||
|
||||
x; // string | 0
|
||||
>x : string | 0
|
||||
|
||||
if (x === "hello" || x === 0) {
|
||||
>x === "hello" || x === 0 : boolean
|
||||
>x === "hello" : boolean
|
||||
>x : string | 0
|
||||
>"hello" : "hello"
|
||||
>x === 0 : boolean
|
||||
>x : string | 0
|
||||
>0 : 0
|
||||
|
||||
x; // 0 | "hello"
|
||||
>x : 0 | "hello"
|
||||
}
|
||||
}
|
||||
if (check1(x)) {
|
||||
>check1(x) : boolean
|
||||
>check1 : (x: unknown) => x is string | 0
|
||||
>x : unknown
|
||||
|
||||
x; // string | 0
|
||||
>x : string | 0
|
||||
|
||||
if (check2(x)) {
|
||||
>check2(x) : boolean
|
||||
>check2 : (x: unknown) => x is 0 | "hello"
|
||||
>x : string | 0
|
||||
|
||||
x; // 0 | "hello"
|
||||
>x : 0 | "hello"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #49588
|
||||
|
||||
function assertRelationIsNullOrStringArray(v: (string | number)[] | null): asserts v is string[] | null {}
|
||||
>assertRelationIsNullOrStringArray : (v: (string | number)[] | null) => asserts v is string[] | null
|
||||
>v : (string | number)[] | null
|
||||
>null : null
|
||||
>null : null
|
||||
|
||||
function f1x(obj: (string | number)[] | null) {
|
||||
>f1x : (obj: (string | number)[] | null) => void
|
||||
>obj : (string | number)[] | null
|
||||
>null : null
|
||||
|
||||
assertRelationIsNullOrStringArray(obj);
|
||||
>assertRelationIsNullOrStringArray(obj) : void
|
||||
>assertRelationIsNullOrStringArray : (v: (string | number)[] | null) => asserts v is string[] | null
|
||||
>obj : (string | number)[] | null
|
||||
|
||||
obj; // string[] | null
|
||||
>obj : string[] | null
|
||||
}
|
||||
|
||||
214
tests/cases/compiler/narrowingUnionToUnion.ts
Normal file
214
tests/cases/compiler/narrowingUnionToUnion.ts
Normal file
@ -0,0 +1,214 @@
|
||||
// @strict: true
|
||||
// @declaration: true
|
||||
|
||||
type Falsy = false | 0 | 0n | '' | null | undefined;
|
||||
|
||||
declare function isFalsy(value: unknown): value is Falsy;
|
||||
|
||||
function fx1(x: string | number | undefined) {
|
||||
if (isFalsy(x)) {
|
||||
x; // "" | 0 | undefined
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T>(x: T | undefined) {
|
||||
if (isFalsy(x)) {
|
||||
x; // T & Falsy | undefined
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends string | number>(x: T) {
|
||||
if (isFalsy(x)) {
|
||||
x; // T & "" | T & 0
|
||||
}
|
||||
}
|
||||
|
||||
declare function isA(obj: unknown): obj is { a: false } | { b: 0 };
|
||||
|
||||
function fx4(obj: { b: number }) {
|
||||
if (isA(obj)) {
|
||||
obj; // { b: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
declare class X { x: string }
|
||||
declare class XS extends X { xs: string }
|
||||
|
||||
declare class Y { y: string }
|
||||
declare class YS extends Y { ys: string }
|
||||
|
||||
declare function isXSorY(obj: unknown): obj is XS | Y;
|
||||
|
||||
function fx5<T extends X>(obj: X | YS, c: typeof XS | typeof Y) {
|
||||
if (obj instanceof c) {
|
||||
obj; // XS | YS
|
||||
}
|
||||
if (isXSorY(obj)) {
|
||||
obj; // XS | YS
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #31156
|
||||
|
||||
declare function isEmptyStrOrUndefined(mixed: any): mixed is "" | undefined;
|
||||
|
||||
function fx10(s: string | undefined) {
|
||||
if (isEmptyStrOrUndefined(s)) {
|
||||
s; // "" | undefined
|
||||
if (s == undefined) {
|
||||
s; // undefined
|
||||
}
|
||||
else {
|
||||
s; // ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #37807
|
||||
|
||||
function f1(x: any): asserts x is number | undefined { }
|
||||
let v1: number | string | undefined;
|
||||
f1(v1);
|
||||
v1; // number | undefined
|
||||
|
||||
function f2(x: any): asserts x is 6 | undefined { }
|
||||
let v2: number | string | undefined;
|
||||
f2(v2);
|
||||
v2; // 6 | undefined
|
||||
|
||||
// #39105
|
||||
|
||||
declare function isEmptyString(value: string): value is '';
|
||||
declare function isMaybeEmptyString(value: string | null | undefined): value is '' | null | undefined;
|
||||
|
||||
declare function isZero(value: number): value is 0;
|
||||
declare function isMaybeZero(value: number | null | undefined): value is 0 | null | undefined;
|
||||
|
||||
declare function isEmptyArray<T>(value: T[]): value is [];
|
||||
declare function isMaybeEmptyArray<T>(value: T[] | null | undefined): value is [] | null | undefined;
|
||||
|
||||
const TEST_CASES = [
|
||||
(value: string) => {
|
||||
if (isEmptyString(value)) {
|
||||
value; // ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
if (isMaybeEmptyString(value)) {
|
||||
value; // ""
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
},
|
||||
(value?: string) => {
|
||||
if (isMaybeEmptyString(value)) {
|
||||
value; // "" | undefined
|
||||
}
|
||||
else {
|
||||
value; // string
|
||||
}
|
||||
},
|
||||
(value: number) => {
|
||||
if (isZero(value)) {
|
||||
value; // 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
if (isMaybeZero(value)) {
|
||||
value; // 0
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
},
|
||||
(value?: number) => {
|
||||
if (isMaybeZero(value)) {
|
||||
value; // 0 | undefined
|
||||
}
|
||||
else {
|
||||
value; // number
|
||||
}
|
||||
},
|
||||
(value: string[]) => {
|
||||
if (isEmptyArray(value)) {
|
||||
value; // []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
value; // []
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
},
|
||||
(value?: string[]) => {
|
||||
if (isMaybeEmptyArray(value)) {
|
||||
value; // [] | undefined
|
||||
}
|
||||
else {
|
||||
value; // string[]
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
// Repro from #42101
|
||||
|
||||
type EmptyString = '' | null | undefined;
|
||||
|
||||
function isEmpty(value: string | EmptyString): value is EmptyString {
|
||||
return value === '' || value === null || value === undefined;
|
||||
}
|
||||
|
||||
let test: string | null | undefined;
|
||||
|
||||
if (isEmpty(test)) {
|
||||
test; // EmptyString
|
||||
}
|
||||
|
||||
// Repro from #43825
|
||||
|
||||
declare function assert<T>(value: any): asserts value is T
|
||||
|
||||
function test1(foo: number | string | boolean) {
|
||||
assert<1 | string>(foo);
|
||||
foo; // string | 1
|
||||
}
|
||||
|
||||
// Repro from #46909
|
||||
|
||||
function check1(x: unknown): x is (string | 0) {
|
||||
return typeof x === "string" || x === 0;
|
||||
}
|
||||
|
||||
function check2(x: unknown): x is ("hello" | 0) {
|
||||
return x === "hello" || x === 0;
|
||||
}
|
||||
|
||||
function test3(x: unknown) {
|
||||
if (typeof x === "string" || x === 0) {
|
||||
x; // string | 0
|
||||
if (x === "hello" || x === 0) {
|
||||
x; // 0 | "hello"
|
||||
}
|
||||
}
|
||||
if (check1(x)) {
|
||||
x; // string | 0
|
||||
if (check2(x)) {
|
||||
x; // 0 | "hello"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #49588
|
||||
|
||||
function assertRelationIsNullOrStringArray(v: (string | number)[] | null): asserts v is string[] | null {}
|
||||
|
||||
function f1x(obj: (string | number)[] | null) {
|
||||
assertRelationIsNullOrStringArray(obj);
|
||||
obj; // string[] | null
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user