TypeScript/tests/cases/compiler/narrowingByTypeofInSwitch.ts
Andrii Dieiev a34fdb203e Better template literals support in checker (#32064)
* Support template literals in enum declarations

* Support template literals in const enum access

* Support template literals in swith with typeof narrowing

* Support template literals in element access discriminant

* Support template literals in ambient module declaration

* Unify symbols for template literals in computed properties

* Unify expression position checks for template literals

* Support template literals in rename and find all references

* Mark computed properties with template literals as write access

* Inline startsWithQuote
2019-09-27 12:04:13 -07:00

303 lines
8.5 KiB
TypeScript

// @strictNullChecks: true
// @strictFunctionTypes: true
function assertNever(x: never) {
return x;
}
function assertNumber(x: number) {
return x;
}
function assertBoolean(x: boolean) {
return x;
}
function assertString(x: string) {
return x;
}
function assertSymbol(x: symbol) {
return x;
}
function assertFunction(x: Function) {
return x;
}
function assertObject(x: object) {
return x;
}
function assertObjectOrNull(x: object | null) {
return x;
}
function assertUndefined(x: undefined) {
return x;
}
function assertAll(x: Basic) {
return x;
}
function assertStringOrNumber(x: string | number) {
return x;
}
function assertBooleanOrObject(x: boolean | object) {
return x;
}
type Basic = number | boolean | string | symbol | object | Function | undefined;
function testUnion(x: Basic) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertFunction(x); return;
case 'symbol': assertSymbol(x); return;
case 'object': assertObject(x); return;
case 'string': assertString(x); return;
case 'undefined': assertUndefined(x); return;
}
assertNever(x);
}
function testExtendsUnion<T extends Basic>(x: T) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertAll(x); return;
case 'symbol': assertSymbol(x); return;
case 'object': assertAll(x); return;
case 'string': assertString(x); return;
case 'undefined': assertUndefined(x); return;
}
assertAll(x);
}
function testAny(x: any) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertFunction(x); return;
case 'symbol': assertSymbol(x); return;
case 'object': assertObject(x); return;
case 'string': assertString(x); return;
case 'undefined': assertUndefined(x); return;
}
assertAll(x); // is any
}
function a1(x: string | object | undefined) {
return x;
}
function testUnionExplicitDefault(x: Basic) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertFunction(x); return;
case 'symbol': assertSymbol(x); return;
default: a1(x); return;
}
}
function testUnionImplicitDefault(x: Basic) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertFunction(x); return;
case 'symbol': assertSymbol(x); return;
}
return a1(x);
}
function testExtendsExplicitDefault<T extends Basic>(x: T) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertAll(x); return;
case 'symbol': assertSymbol(x); return;
default: assertAll(x); return;
}
}
function testExtendsImplicitDefault<T extends Basic>(x: T) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertAll(x); return;
case 'symbol': assertSymbol(x); return;
}
return assertAll(x);
}
type L = (x: number) => string;
type R = { x: string, y: number }
function exhaustiveChecks(x: number | string | L | R): string {
switch (typeof x) {
case 'number': return x.toString(2);
case 'string': return x;
case 'function': return x(42);
case 'object': return x.x;
}
}
function exhaustiveChecksGenerics<T extends L | R | number | string>(x: T): string {
switch (typeof x) {
case 'number': return x.toString(2);
case 'string': return x;
case 'function': return (x as L)(42); // Can't narrow generic
case 'object': return (x as R).x; // Can't narrow generic
}
}
function multipleGeneric<X extends L, Y extends R>(xy: X | Y): [X, string] | [Y, number] {
switch (typeof xy) {
case 'function': return [xy, xy(42)];
case 'object': return [xy, xy.y];
default: return assertNever(xy);
}
}
function multipleGenericFuse<X extends L | number, Y extends R | number>(xy: X | Y): [X, number] | [Y, string] | [(X | Y)] {
switch (typeof xy) {
case 'function': return [xy, 1];
case 'object': return [xy, 'two'];
case 'number': return [xy]
}
}
function multipleGenericExhaustive<X extends L, Y extends R>(xy: X | Y): [X, string] | [Y, number] {
switch (typeof xy) {
case 'object': return [xy, xy.y];
case 'function': return [xy, xy(42)];
}
}
function switchOrdering(x: string | number | boolean) {
switch (typeof x) {
case 'string': return assertString(x);
case 'number': return assertNumber(x);
case 'boolean': return assertBoolean(x);
case 'number': return assertNever(x);
}
}
function switchOrderingWithDefault(x: string | number | boolean) {
function local(y: string | number | boolean) {
return x;
}
switch (typeof x) {
case 'string':
case 'number':
default: return local(x)
case 'string': return assertNever(x);
case 'number': return assertNever(x);
}
}
function fallThroughTest(x: string | number | boolean | object) {
switch (typeof x) {
case 'number':
assertNumber(x)
case 'string':
assertStringOrNumber(x)
break;
default:
assertObject(x);
case 'number':
case 'boolean':
assertBooleanOrObject(x);
break;
}
}
function unknownNarrowing(x: unknown) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertFunction(x); return;
case 'symbol': assertSymbol(x); return;
case 'object': assertObjectOrNull(x); return;
case 'string': assertString(x); return;
case 'undefined': assertUndefined(x); return;
}
}
function keyofNarrowing<S extends { [K in keyof S]: string }>(k: keyof S) {
function assertKeyofS(k1: keyof S) { }
switch (typeof k) {
case 'number': assertNumber(k); assertKeyofS(k); return;
case 'symbol': assertSymbol(k); assertKeyofS(k); return;
case 'string': assertString(k); assertKeyofS(k); return;
}
}
function narrowingNarrows(x: {} | undefined) {
switch (typeof x) {
case 'number': assertNumber(x); return;
case 'boolean': assertBoolean(x); return;
case 'function': assertFunction(x); return;
case 'symbol': assertSymbol(x); return;
case 'object': const _: {} = x; return;
case 'string': assertString(x); return;
case 'undefined': assertUndefined(x); return;
case 'number': assertNever(x); return;
default: const _y: {} = x; return;
}
}
/* Template literals */
function testUnionWithTempalte(x: Basic) {
switch (typeof x) {
case `number`: assertNumber(x); return;
case `boolean`: assertBoolean(x); return;
case `function`: assertFunction(x); return;
case `symbol`: assertSymbol(x); return;
case `object`: assertObject(x); return;
case `string`: assertString(x); return;
case `undefined`: assertUndefined(x); return;
}
assertNever(x);
}
function fallThroughTestWithTempalte(x: string | number | boolean | object) {
switch (typeof x) {
case `number`:
assertNumber(x)
case `string`:
assertStringOrNumber(x)
break;
default:
assertObject(x);
case `number`:
case `boolean`:
assertBooleanOrObject(x);
break;
}
}
function keyofNarrowingWithTemplate<S extends { [K in keyof S]: string }>(k: keyof S) {
function assertKeyofS(k1: keyof S) { }
switch (typeof k) {
case `number`: assertNumber(k); assertKeyofS(k); return;
case `symbol`: assertSymbol(k); assertKeyofS(k); return;
case `string`: assertString(k); assertKeyofS(k); return;
}
}
/* Both string literals and template literals */
function multipleGenericFuseWithBoth<X extends L | number, Y extends R | number>(xy: X | Y): [X, number] | [Y, string] | [(X | Y)] {
switch (typeof xy) {
case `function`: return [xy, 1];
case 'object': return [xy, 'two'];
case `number`: return [xy]
}
}