316 lines
9 KiB
TypeScript
316 lines
9 KiB
TypeScript
// @strict: 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;
|
|
}
|
|
}
|
|
|
|
function narrowingNarrows2(x: true | 3 | 'hello' | undefined) {
|
|
switch (typeof x) {
|
|
case 'number': assertNumber(x); return;
|
|
case 'boolean': assertBoolean(x); return;
|
|
case 'function': assertNever(x); return;
|
|
case 'symbol': assertNever(x); return;
|
|
case 'object': const _: {} = assertNever(x); return;
|
|
case 'string': assertString(x); return;
|
|
case 'undefined': assertUndefined(x); return;
|
|
case 'number': assertNever(x); return;
|
|
default: const _y: {} = assertNever(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]
|
|
}
|
|
}
|