Skip to content

Commit

Permalink
Cherry-pick changes for narrowing to tagged literal types.
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielRosenwasser committed Dec 2, 2022
1 parent e7a02f4 commit b4d382b
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -21630,7 +21630,10 @@ namespace ts {
}

function isUnitLikeType(type: Type): boolean {
return isUnitType(getBaseConstraintOrType(type));
// Intersections that reduce to 'never' (e.g. 'T & null' where 'T extends {}') are not unit types.
const t = getBaseConstraintOrType(type);
// Scan intersections such that tagged literal types are considered unit types.
return t.flags & TypeFlags.Intersection ? some((t as IntersectionType).types, isUnitType) : isUnitType(t);
}

function extractUnitType(type: Type) {
Expand Down
22 changes: 22 additions & 0 deletions tests/baselines/reference/unknownControlFlow.errors.txt
Expand Up @@ -444,4 +444,26 @@ tests/cases/conformance/types/unknown/unknownControlFlow.ts(293,5): error TS2345
function x<T_AB extends AB>(x: T_AB & undefined, y: any) {
let r2: never = y as T_AB & undefined;
}

// Repro from #51538

type Left = 'left';
type Right = 'right' & { right: 'right' };
type Either = Left | Right;

function assertNever(v: never): never {
throw new Error('never');
}

function fx20(value: Either) {
if (value === 'left') {
const foo: 'left' = value;
}
else if (value === 'right') {
const bar: 'right' = value;
}
else {
assertNever(value);
}
}

43 changes: 43 additions & 0 deletions tests/baselines/reference/unknownControlFlow.js
Expand Up @@ -427,6 +427,28 @@ type AB = "A" | "B";
function x<T_AB extends AB>(x: T_AB & undefined, y: any) {
let r2: never = y as T_AB & undefined;
}

// Repro from #51538

type Left = 'left';
type Right = 'right' & { right: 'right' };
type Either = Left | Right;

function assertNever(v: never): never {
throw new Error('never');
}

function fx20(value: Either) {
if (value === 'left') {
const foo: 'left' = value;
}
else if (value === 'right') {
const bar: 'right' = value;
}
else {
assertNever(value);
}
}


//// [unknownControlFlow.js]
Expand Down Expand Up @@ -772,6 +794,20 @@ function doSomething2(value) {
function x(x, y) {
var r2 = y;
}
function assertNever(v) {
throw new Error('never');
}
function fx20(value) {
if (value === 'left') {
var foo_1 = value;
}
else if (value === 'right') {
var bar = value;
}
else {
assertNever(value);
}
}


//// [unknownControlFlow.d.ts]
Expand Down Expand Up @@ -844,3 +880,10 @@ type R<T extends keyof TypeA> = T extends keyof TypeB ? [TypeA[T], TypeB[T]] : n
type R2<T extends PropertyKey> = T extends keyof TypeA ? T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never : never;
type AB = "A" | "B";
declare function x<T_AB extends AB>(x: T_AB & undefined, y: any): void;
type Left = 'left';
type Right = 'right' & {
right: 'right';
};
type Either = Left | Right;
declare function assertNever(v: never): never;
declare function fx20(value: Either): void;
48 changes: 48 additions & 0 deletions tests/baselines/reference/unknownControlFlow.symbols
Expand Up @@ -995,3 +995,51 @@ function x<T_AB extends AB>(x: T_AB & undefined, y: any) {
>T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 425, 11))
}

// Repro from #51538

type Left = 'left';
>Left : Symbol(Left, Decl(unknownControlFlow.ts, 427, 1))

type Right = 'right' & { right: 'right' };
>Right : Symbol(Right, Decl(unknownControlFlow.ts, 431, 19))
>right : Symbol(right, Decl(unknownControlFlow.ts, 432, 24))

type Either = Left | Right;
>Either : Symbol(Either, Decl(unknownControlFlow.ts, 432, 42))
>Left : Symbol(Left, Decl(unknownControlFlow.ts, 427, 1))
>Right : Symbol(Right, Decl(unknownControlFlow.ts, 431, 19))

function assertNever(v: never): never {
>assertNever : Symbol(assertNever, Decl(unknownControlFlow.ts, 433, 27))
>v : Symbol(v, Decl(unknownControlFlow.ts, 435, 21))

throw new Error('never');
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}

function fx20(value: Either) {
>fx20 : Symbol(fx20, Decl(unknownControlFlow.ts, 437, 1))
>value : Symbol(value, Decl(unknownControlFlow.ts, 439, 14))
>Either : Symbol(Either, Decl(unknownControlFlow.ts, 432, 42))

if (value === 'left') {
>value : Symbol(value, Decl(unknownControlFlow.ts, 439, 14))

const foo: 'left' = value;
>foo : Symbol(foo, Decl(unknownControlFlow.ts, 441, 13))
>value : Symbol(value, Decl(unknownControlFlow.ts, 439, 14))
}
else if (value === 'right') {
>value : Symbol(value, Decl(unknownControlFlow.ts, 439, 14))

const bar: 'right' = value;
>bar : Symbol(bar, Decl(unknownControlFlow.ts, 444, 13))
>value : Symbol(value, Decl(unknownControlFlow.ts, 439, 14))
}
else {
assertNever(value);
>assertNever : Symbol(assertNever, Decl(unknownControlFlow.ts, 433, 27))
>value : Symbol(value, Decl(unknownControlFlow.ts, 439, 14))
}
}

52 changes: 52 additions & 0 deletions tests/baselines/reference/unknownControlFlow.types
Expand Up @@ -1076,3 +1076,55 @@ function x<T_AB extends AB>(x: T_AB & undefined, y: any) {
>y : any
}

// Repro from #51538

type Left = 'left';
>Left : "left"

type Right = 'right' & { right: 'right' };
>Right : "right" & { right: 'right'; }
>right : "right"

type Either = Left | Right;
>Either : Right | "left"

function assertNever(v: never): never {
>assertNever : (v: never) => never
>v : never

throw new Error('never');
>new Error('never') : Error
>Error : ErrorConstructor
>'never' : "never"
}

function fx20(value: Either) {
>fx20 : (value: Either) => void
>value : Either

if (value === 'left') {
>value === 'left' : boolean
>value : Either
>'left' : "left"

const foo: 'left' = value;
>foo : "left"
>value : "left"
}
else if (value === 'right') {
>value === 'right' : boolean
>value : Right
>'right' : "right"

const bar: 'right' = value;
>bar : "right"
>value : Right
}
else {
assertNever(value);
>assertNever(value) : never
>assertNever : (v: never) => never
>value : never
}
}

22 changes: 22 additions & 0 deletions tests/cases/conformance/types/unknown/unknownControlFlow.ts
Expand Up @@ -429,3 +429,25 @@ type AB = "A" | "B";
function x<T_AB extends AB>(x: T_AB & undefined, y: any) {
let r2: never = y as T_AB & undefined;
}

// Repro from #51538

type Left = 'left';
type Right = 'right' & { right: 'right' };
type Either = Left | Right;

function assertNever(v: never): never {
throw new Error('never');
}

function fx20(value: Either) {
if (value === 'left') {
const foo: 'left' = value;
}
else if (value === 'right') {
const bar: 'right' = value;
}
else {
assertNever(value);
}
}

0 comments on commit b4d382b

Please sign in to comment.