diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4c0e7a036dbf4..a86b1ed708d9f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2218,7 +2218,10 @@ namespace ts { ObjectType = Class | Interface | Reference | Tuple | Anonymous, UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, - Narrowable = Any | ObjectType | Union | TypeParameter, + + // 'Narrowable' types are types where narrowing actually narrows. + // This *should* be every type other than null, undefined, void, and never + Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | Boolean | ESSymbol, /* @internal */ RequiresWidening = ContainsWideningType | ContainsObjectLiteral, /* @internal */ diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js new file mode 100644 index 0000000000000..a4cba6f4ab5c1 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js @@ -0,0 +1,38 @@ +//// [typeGuardNarrowsPrimitiveIntersection.ts] +type Tag = {__tag: any}; +declare function isNonBlank(value: string) : value is (string & Tag); +declare function doThis(value: string & Tag): void; +declare function doThat(value: string) : void; +let value: string; +if (isNonBlank(value)) { + doThis(value); +} else { + doThat(value); +} + + +const enum Tag2 {} +declare function isNonBlank2(value: string) : value is (string & Tag2); +declare function doThis2(value: string & Tag2): void; +declare function doThat2(value: string) : void; +if (isNonBlank2(value)) { + doThis2(value); +} else { + doThat2(value); +} + + +//// [typeGuardNarrowsPrimitiveIntersection.js] +var value; +if (isNonBlank(value)) { + doThis(value); +} +else { + doThat(value); +} +if (isNonBlank2(value)) { + doThis2(value); +} +else { + doThat2(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols new file mode 100644 index 0000000000000..da507fb94c5dc --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols @@ -0,0 +1,70 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts === +type Tag = {__tag: any}; +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) +>__tag : Symbol(__tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 12)) + +declare function isNonBlank(value: string) : value is (string & Tag); +>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28)) +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) + +declare function doThis(value: string & Tag): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 24)) +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 3, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +if (isNonBlank(value)) { +>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) +} + + +const enum Tag2 {} +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function isNonBlank2(value: string) : value is (string & Tag2); +>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29)) +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function doThis2(value: string & Tag2): void; +>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 25)) +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function doThat2(value: string) : void; +>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 15, 25)) + +if (isNonBlank2(value)) { +>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + + doThis2(value); +>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +} else { + doThat2(value); +>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) +} + diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types new file mode 100644 index 0000000000000..478363669b461 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types @@ -0,0 +1,76 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts === +type Tag = {__tag: any}; +>Tag : { __tag: any; } +>__tag : any + +declare function isNonBlank(value: string) : value is (string & Tag); +>isNonBlank : (value: string) => value is string & { __tag: any; } +>value : string +>value : any +>Tag : { __tag: any; } + +declare function doThis(value: string & Tag): void; +>doThis : (value: string & { __tag: any; }) => void +>value : string & { __tag: any; } +>Tag : { __tag: any; } + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isNonBlank(value)) { +>isNonBlank(value) : boolean +>isNonBlank : (value: string) => value is string & { __tag: any; } +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: string & { __tag: any; }) => void +>value : string & { __tag: any; } + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + +const enum Tag2 {} +>Tag2 : Tag2 + +declare function isNonBlank2(value: string) : value is (string & Tag2); +>isNonBlank2 : (value: string) => value is string & Tag2 +>value : string +>value : any +>Tag2 : Tag2 + +declare function doThis2(value: string & Tag2): void; +>doThis2 : (value: string & Tag2) => void +>value : string & Tag2 +>Tag2 : Tag2 + +declare function doThat2(value: string) : void; +>doThat2 : (value: string) => void +>value : string + +if (isNonBlank2(value)) { +>isNonBlank2(value) : boolean +>isNonBlank2 : (value: string) => value is string & Tag2 +>value : string + + doThis2(value); +>doThis2(value) : void +>doThis2 : (value: string & Tag2) => void +>value : string & Tag2 + +} else { + doThat2(value); +>doThat2(value) : void +>doThat2 : (value: string) => void +>value : string +} + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js new file mode 100644 index 0000000000000..fdd2ee3fb7f75 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js @@ -0,0 +1,21 @@ +//// [typeGuardNarrowsToLiteralType.ts] +declare function isFoo(value: string) : value is "foo"; +declare function doThis(value: "foo"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + + + +//// [typeGuardNarrowsToLiteralType.js] +var value; +if (isFoo(value)) { + doThis(value); +} +else { + doThat(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols new file mode 100644 index 0000000000000..0d0ddc5f00dc1 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts === +declare function isFoo(value: string) : value is "foo"; +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23)) + +declare function doThis(value: "foo"): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 1, 24)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 2, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + +if (isFoo(value)) { +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) +} + + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types new file mode 100644 index 0000000000000..9835206deb9f4 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types @@ -0,0 +1,35 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts === +declare function isFoo(value: string) : value is "foo"; +>isFoo : (value: string) => value is "foo" +>value : string +>value : any + +declare function doThis(value: "foo"): void; +>doThis : (value: "foo") => void +>value : "foo" + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isFoo(value)) { +>isFoo(value) : boolean +>isFoo : (value: string) => value is "foo" +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: "foo") => void +>value : "foo" + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js new file mode 100644 index 0000000000000..715b362c8e0c2 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js @@ -0,0 +1,21 @@ +//// [typeGuardNarrowsToLiteralTypeUnion.ts] +declare function isFoo(value: string) : value is ("foo" | "bar"); +declare function doThis(value: "foo" | "bar"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + + + +//// [typeGuardNarrowsToLiteralTypeUnion.js] +var value; +if (isFoo(value)) { + doThis(value); +} +else { + doThat(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols new file mode 100644 index 0000000000000..356fa06a06c4a --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts === +declare function isFoo(value: string) : value is ("foo" | "bar"); +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23)) + +declare function doThis(value: "foo" | "bar"): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 24)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 2, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + +if (isFoo(value)) { +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) +} + + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types new file mode 100644 index 0000000000000..321a8861d55f5 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types @@ -0,0 +1,35 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts === +declare function isFoo(value: string) : value is ("foo" | "bar"); +>isFoo : (value: string) => value is "foo" | "bar" +>value : string +>value : any + +declare function doThis(value: "foo" | "bar"): void; +>doThis : (value: "foo" | "bar") => void +>value : "foo" | "bar" + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isFoo(value)) { +>isFoo(value) : boolean +>isFoo : (value: string) => value is "foo" | "bar" +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: "foo" | "bar") => void +>value : "foo" | "bar" + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts new file mode 100644 index 0000000000000..376a78275c83b --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts @@ -0,0 +1,21 @@ +type Tag = {__tag: any}; +declare function isNonBlank(value: string) : value is (string & Tag); +declare function doThis(value: string & Tag): void; +declare function doThat(value: string) : void; +let value: string; +if (isNonBlank(value)) { + doThis(value); +} else { + doThat(value); +} + + +const enum Tag2 {} +declare function isNonBlank2(value: string) : value is (string & Tag2); +declare function doThis2(value: string & Tag2): void; +declare function doThat2(value: string) : void; +if (isNonBlank2(value)) { + doThis2(value); +} else { + doThat2(value); +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts new file mode 100644 index 0000000000000..3b7d5bba21ab7 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts @@ -0,0 +1,10 @@ +declare function isFoo(value: string) : value is "foo"; +declare function doThis(value: "foo"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts new file mode 100644 index 0000000000000..8f4726160f4b2 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts @@ -0,0 +1,10 @@ +declare function isFoo(value: string) : value is ("foo" | "bar"); +declare function doThis(value: "foo" | "bar"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} +