Fix isUnitLikeType to (again) handle tagged literal types (#51545)

* Fix isUnitLikeType to (again) handle tagged literal types

* Add regression test
This commit is contained in:
Anders Hejlsberg 2022-11-30 13:30:52 -08:00 committed by GitHub
parent 16edc29bc9
commit 8036b149a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 191 additions and 1 deletions

View File

@ -22633,7 +22633,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
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) {

View File

@ -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);
}
}

View File

@ -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]
@ -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]
@ -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;

View File

@ -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))
}
}

View File

@ -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
}
}

View File

@ -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);
}
}