Add typeof-for-switch

Initial draft that works for union types

First draft of PR ready code with tests

Revert changed line for testing

Add exhaustiveness checking and move narrowByTypeOfWitnesses

Try caching mechanism

Comment out exhaustiveness checking to find perf regression

Re-enable exhaustiveness checking for typeof switches

Check if changes to narrowByTypeOfWitnesses fix perf alone.

Improve switch narrowing:

+ Take into account repeated clauses in the switch.
+ Handle unions of constrained type parameters.

Add more tests

Comments

Revert back to if-like behaviour

Remove redundant checks and simplify exhaustiveness checks

Change comment for narrowBySwitchOnTypeOf

Reduce implied type with getAssignmentReducedType

Remove any annotations
This commit is contained in:
Jack Williams
2018-02-13 01:14:47 +00:00
parent b271df1639
commit 0d79831ead
6 changed files with 1975 additions and 0 deletions

View File

@@ -737,6 +737,8 @@ namespace ts {
return isNarrowingBinaryExpression(<BinaryExpression>expr);
case SyntaxKind.PrefixUnaryExpression:
return (<PrefixUnaryExpression>expr).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((<PrefixUnaryExpression>expr).operand);
case SyntaxKind.TypeOfExpression:
return isNarrowingExpression((<TypeOfExpression>expr).expression);
}
return false;
}

View File

@@ -12840,6 +12840,21 @@ namespace ts {
return links.switchTypes;
}
function getSwitchClauseTypeOfWitnesses(switchStatement: SwitchStatement): (string | undefined)[] {
const witnesses: (string | undefined)[] = [];
for (const clause of switchStatement.caseBlock.clauses) {
if (clause.kind === SyntaxKind.CaseClause) {
if (clause.expression.kind === SyntaxKind.StringLiteral) {
witnesses.push((clause.expression as StringLiteral).text);
continue;
}
return emptyArray;
}
witnesses.push(/*explicitDefaultStatement*/ undefined);
}
return witnesses;
}
function eachTypeContainedIn(source: Type, types: Type[]) {
return source.flags & TypeFlags.Union ? !forEach((<UnionType>source).types, t => !contains(types, t)) : contains(types, source);
}
@@ -13253,6 +13268,9 @@ namespace ts {
else if (isMatchingReferenceDiscriminant(expr, type)) {
type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
}
else if (expr.kind === SyntaxKind.TypeOfExpression && isMatchingReference(reference, (expr as TypeOfExpression).expression)) {
type = narrowBySwitchOnTypeOf(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
}
return createFlowType(type, isIncomplete(flowType));
}
@@ -13549,6 +13567,57 @@ namespace ts {
return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]);
}
function narrowBySwitchOnTypeOf(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): Type {
const switchWitnesses = getSwitchClauseTypeOfWitnesses(switchStatement);
if (!switchWitnesses.length) {
return type;
}
const clauseWitnesses = switchWitnesses.slice(clauseStart, clauseEnd);
// Equal start and end denotes implicit fallthrough; undefined marks explicit default clause
const hasDefaultClause = clauseStart === clauseEnd || contains(clauseWitnesses, /*explicitDefaultStatement*/ undefined);
const switchFacts = getFactsFromTypeofSwitch(clauseStart, clauseEnd, switchWitnesses, hasDefaultClause);
// The implied type is the raw type suggested by a
// value being caught in this clause.
// - If there is a default the implied type is not used.
// - Otherwise, take the union of the types in the
// clause. We narrow the union using facts to remove
// types that appear multiple types and are
// unreachable.
// Example:
//
// switch (typeof x) {
// case 'number':
// case 'string': break;
// default: break;
// case 'number':
// case 'boolean': break
// }
//
// The implied type of the first clause number | string.
// The implied type of the second clause is string (but this doesn't get used).
// The implied type of the third clause is boolean (number has already be caught).
if (!(hasDefaultClause || (type.flags & TypeFlags.Union))) {
let impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => typeofTypesByName.get(text) || neverType)), switchFacts);
if (impliedType.flags & TypeFlags.Union) {
impliedType = getAssignmentReducedType(impliedType as UnionType, getBaseConstraintOfType(type) || type);
}
if (!(impliedType.flags & TypeFlags.Never)) {
if (isTypeSubtypeOf(impliedType, type)) {
return impliedType;
}
if (type.flags & TypeFlags.Instantiable) {
const constraint = getBaseConstraintOfType(type) || anyType;
if (isTypeSubtypeOf(impliedType, constraint)) {
return getIntersectionType([type, impliedType]);
}
}
}
}
return hasDefaultClause ?
filterType(type, t => (getTypeFacts(t) & switchFacts) === switchFacts) :
getTypeWithFacts(type, switchFacts);
}
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
const left = getReferenceCandidate(expr.left);
if (!isMatchingReference(reference, left)) {
@@ -18944,10 +19013,60 @@ namespace ts {
: Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
/**
* Collect the TypeFacts learned from a typeof switch with
* total clauses `witnesses`, and the active clause ranging
* from `start` to `end`. Parameter `hasDefault` denotes
* whether the active clause contains a default clause.
*/
function getFactsFromTypeofSwitch(start: number, end: number, witnesses: (string | undefined)[], hasDefault: boolean): TypeFacts {
let facts: TypeFacts = TypeFacts.None;
// When in the default we only collect inequality facts
// because default is 'in theory' a set of infinite
// equalities.
if (hasDefault) {
// Value is not equal to any types after the active clause.
for (let i = end; i < witnesses.length; i++) {
facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject;
}
// Remove inequalities for types that appear in the
// active clause because they appear before other
// types collected so far.
for (let i = start; i < end; i++) {
facts &= ~(typeofNEFacts.get(witnesses[i]) || 0);
}
// Add inequalities for types before the active clause unconditionally.
for (let i = 0; i < start; i++) {
facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject;
}
}
// When in an active clause without default the set of
// equalities is finite.
else {
// Add equalities for all types in the active clause.
for (let i = start; i < end; i++) {
facts |= typeofEQFacts.get(witnesses[i]) || TypeFacts.TypeofEQHostObject;
}
// Remove equalities for types that appear before the
// active clause.
for (let i = 0; i < start; i++) {
facts &= ~(typeofEQFacts.get(witnesses[i]) || 0);
}
}
return facts;
}
function isExhaustiveSwitchStatement(node: SwitchStatement): boolean {
if (!node.possiblyExhaustive) {
return false;
}
if (node.expression.kind === SyntaxKind.TypeOfExpression) {
const operandType = getTypeOfExpression((node.expression as TypeOfExpression).expression);
// Type is not equal to every type in the switch.
const notEqualFacts = getFactsFromTypeofSwitch(0, 0, getSwitchClauseTypeOfWitnesses(node), /*hasDefault*/ true);
const type = getBaseConstraintOfType(operandType) || operandType;
return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never);
}
const type = getTypeOfExpression(node.expression);
if (!isLiteralType(type)) {
return false;

View File

@@ -0,0 +1,427 @@
//// [narrowingByTypeofInSwitch.ts]
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 assertUndefined(x: undefined) {
return x;
}
function assertAll(x: Basic) {
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);
}
}
//// [narrowingByTypeofInSwitch.js]
function assertNever(x) {
return x;
}
function assertNumber(x) {
return x;
}
function assertBoolean(x) {
return x;
}
function assertString(x) {
return x;
}
function assertSymbol(x) {
return x;
}
function assertFunction(x) {
return x;
}
function assertObject(x) {
return x;
}
function assertUndefined(x) {
return x;
}
function assertAll(x) {
return x;
}
function testUnion(x) {
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(x) {
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) {
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) {
return x;
}
function testUnionExplicitDefault(x) {
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) {
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(x) {
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(x) {
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);
}
function exhaustiveChecks(x) {
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(x) {
switch (typeof x) {
case 'number': return x.toString(2);
case 'string': return x;
case 'function': return x(42); // Can't narrow generic
case 'object': return x.x; // Can't narrow generic
}
}
function multipleGeneric(xy) {
switch (typeof xy) {
case 'function': return [xy, xy(42)];
case 'object': return [xy, xy.y];
default: return assertNever(xy);
}
}
function multipleGenericFuse(xy) {
switch (typeof xy) {
case 'function': return [xy, 1];
case 'object': return [xy, 'two'];
case 'number': return [xy];
}
}
function multipleGenericExhaustive(xy) {
switch (typeof xy) {
case 'object': return [xy, xy.y];
case 'function': return [xy, xy(42)];
}
}
function switchOrdering(x) {
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) {
function local(y) {
return x;
}
switch (typeof x) {
case 'string':
case 'number':
default: return local(x);
case 'string': return assertNever(x);
case 'number': return assertNever(x);
}
}

View File

@@ -0,0 +1,542 @@
=== tests/cases/compiler/narrowingByTypeofInSwitch.ts ===
function assertNever(x: never) {
>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 0, 21))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 0, 21))
}
function assertNumber(x: number) {
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 4, 22))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 4, 22))
}
function assertBoolean(x: boolean) {
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 8, 23))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 8, 23))
}
function assertString(x: string) {
>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 12, 22))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 12, 22))
}
function assertSymbol(x: symbol) {
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 16, 22))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 16, 22))
}
function assertFunction(x: Function) {
>assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 20, 24))
>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 20, 24))
}
function assertObject(x: object) {
>assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 24, 22))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 24, 22))
}
function assertUndefined(x: undefined) {
>assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 26, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 28, 25))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 28, 25))
}
function assertAll(x: Basic) {
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 32, 19))
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 32, 19))
}
type Basic = number | boolean | string | symbol | object | Function | undefined;
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
function testUnion(x: Basic) {
>testUnion : Symbol(testUnion, Decl(narrowingByTypeofInSwitch.ts, 36, 80))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
case 'number': assertNumber(x); return;
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
case 'boolean': assertBoolean(x); return;
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
case 'function': assertFunction(x); return;
>assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
case 'symbol': assertSymbol(x); return;
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
case 'object': assertObject(x); return;
>assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
case 'string': assertString(x); return;
>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
case 'undefined': assertUndefined(x); return;
>assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 26, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
}
assertNever(x);
>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19))
}
function testExtendsUnion<T extends Basic>(x: T) {
>testExtendsUnion : Symbol(testExtendsUnion, Decl(narrowingByTypeofInSwitch.ts, 49, 1))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 51, 26))
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 51, 26))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
case 'number': assertNumber(x); return;
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
case 'boolean': assertBoolean(x); return;
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
case 'function': assertAll(x); return;
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
case 'symbol': assertSymbol(x); return;
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
case 'object': assertAll(x); return;
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
case 'string': assertString(x); return;
>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
case 'undefined': assertUndefined(x); return;
>assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 26, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
}
assertAll(x);
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43))
}
function testAny(x: any) {
>testAny : Symbol(testAny, Decl(narrowingByTypeofInSwitch.ts, 62, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
case 'number': assertNumber(x); return;
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
case 'boolean': assertBoolean(x); return;
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
case 'function': assertFunction(x); return;
>assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
case 'symbol': assertSymbol(x); return;
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
case 'object': assertObject(x); return;
>assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
case 'string': assertString(x); return;
>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
case 'undefined': assertUndefined(x); return;
>assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 26, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
}
assertAll(x); // is any
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17))
}
function a1(x: string | object | undefined) {
>a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 75, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 77, 12))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 77, 12))
}
function testUnionExplicitDefault(x: Basic) {
>testUnionExplicitDefault : Symbol(testUnionExplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 79, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34))
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34))
case 'number': assertNumber(x); return;
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34))
case 'boolean': assertBoolean(x); return;
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34))
case 'function': assertFunction(x); return;
>assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34))
case 'symbol': assertSymbol(x); return;
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34))
default: a1(x); return;
>a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 75, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34))
}
}
function testUnionImplicitDefault(x: Basic) {
>testUnionImplicitDefault : Symbol(testUnionImplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 89, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34))
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34))
case 'number': assertNumber(x); return;
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34))
case 'boolean': assertBoolean(x); return;
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34))
case 'function': assertFunction(x); return;
>assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34))
case 'symbol': assertSymbol(x); return;
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34))
}
return a1(x);
>a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 75, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34))
}
function testExtendsExplicitDefault<T extends Basic>(x: T) {
>testExtendsExplicitDefault : Symbol(testExtendsExplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 99, 1))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 101, 36))
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 101, 36))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53))
case 'number': assertNumber(x); return;
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53))
case 'boolean': assertBoolean(x); return;
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53))
case 'function': assertAll(x); return;
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53))
case 'symbol': assertSymbol(x); return;
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53))
default: assertAll(x); return;
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53))
}
}
function testExtendsImplicitDefault<T extends Basic>(x: T) {
>testExtendsImplicitDefault : Symbol(testExtendsImplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 110, 1))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 112, 36))
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 112, 36))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53))
case 'number': assertNumber(x); return;
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53))
case 'boolean': assertBoolean(x); return;
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53))
case 'function': assertAll(x); return;
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53))
case 'symbol': assertSymbol(x); return;
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53))
}
return assertAll(x);
>assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53))
}
type L = (x: number) => string;
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 122, 10))
type R = { x: string, y: number }
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 122, 31))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 123, 10))
>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 123, 21))
function exhaustiveChecks(x: number | string | L | R): string {
>exhaustiveChecks : Symbol(exhaustiveChecks, Decl(narrowingByTypeofInSwitch.ts, 123, 33))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26))
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1))
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 122, 31))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26))
case 'number': return x.toString(2);
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
case 'string': return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26))
case 'function': return x(42);
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26))
case 'object': return x.x;
>x.x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 123, 10))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 123, 10))
}
}
function exhaustiveChecksGenerics<T extends L | R | number | string>(x: T): string {
>exhaustiveChecksGenerics : Symbol(exhaustiveChecksGenerics, Decl(narrowingByTypeofInSwitch.ts, 132, 1))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 134, 34))
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1))
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 122, 31))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 134, 69))
>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 134, 34))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 134, 69))
case 'number': return x.toString(2);
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --) ... and 2 more)
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 134, 69))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --) ... and 2 more)
case 'string': return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 134, 69))
case 'function': return (x as L)(42); // Can't narrow generic
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 134, 69))
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1))
case 'object': return (x as R).x; // Can't narrow generic
>(x as R).x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 123, 10))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 134, 69))
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 122, 31))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 123, 10))
}
}
function multipleGeneric<X extends L, Y extends R>(xy: X | Y): [X, string] | [Y, number] {
>multipleGeneric : Symbol(multipleGeneric, Decl(narrowingByTypeofInSwitch.ts, 141, 1))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 143, 25))
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 143, 37))
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 122, 31))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 143, 25))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 143, 37))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 143, 25))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 143, 37))
switch (typeof xy) {
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51))
case 'function': return [xy, xy(42)];
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51))
case 'object': return [xy, xy.y];
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51))
>xy.y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 123, 21))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51))
>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 123, 21))
default: return assertNever(xy);
>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51))
}
}
function multipleGenericFuse<X extends L | number, Y extends R | number>(xy: X | Y): [X, number] | [Y, string] | [(X | Y)] {
>multipleGenericFuse : Symbol(multipleGenericFuse, Decl(narrowingByTypeofInSwitch.ts, 149, 1))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 151, 29))
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 151, 50))
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 122, 31))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 151, 29))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 151, 50))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 151, 29))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 151, 50))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 151, 29))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 151, 50))
switch (typeof xy) {
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73))
case 'function': return [xy, 1];
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73))
case 'object': return [xy, 'two'];
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73))
case 'number': return [xy]
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73))
}
}
function multipleGenericExhaustive<X extends L, Y extends R>(xy: X | Y): [X, string] | [Y, number] {
>multipleGenericExhaustive : Symbol(multipleGenericExhaustive, Decl(narrowingByTypeofInSwitch.ts, 157, 1))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 159, 35))
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 159, 47))
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 122, 31))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 159, 35))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 159, 47))
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 159, 35))
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 159, 47))
switch (typeof xy) {
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61))
case 'object': return [xy, xy.y];
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61))
>xy.y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 123, 21))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61))
>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 123, 21))
case 'function': return [xy, xy(42)];
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61))
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61))
}
}
function switchOrdering(x: string | number | boolean) {
>switchOrdering : Symbol(switchOrdering, Decl(narrowingByTypeofInSwitch.ts, 164, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24))
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24))
case 'string': return assertString(x);
>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24))
case 'number': return assertNumber(x);
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24))
case 'boolean': return assertBoolean(x);
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24))
case 'number': return assertNever(x);
>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24))
}
}
function switchOrderingWithDefault(x: string | number | boolean) {
>switchOrderingWithDefault : Symbol(switchOrderingWithDefault, Decl(narrowingByTypeofInSwitch.ts, 173, 1))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35))
function local(y: string | number | boolean) {
>local : Symbol(local, Decl(narrowingByTypeofInSwitch.ts, 175, 66))
>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 176, 19))
return x;
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35))
}
switch (typeof x) {
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35))
case 'string':
case 'number':
default: return local(x)
>local : Symbol(local, Decl(narrowingByTypeofInSwitch.ts, 175, 66))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35))
case 'string': return assertNever(x);
>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35))
case 'number': return assertNever(x);
>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0))
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35))
}
}

View File

@@ -0,0 +1,695 @@
=== tests/cases/compiler/narrowingByTypeofInSwitch.ts ===
function assertNever(x: never) {
>assertNever : (x: never) => never
>x : never
return x;
>x : never
}
function assertNumber(x: number) {
>assertNumber : (x: number) => number
>x : number
return x;
>x : number
}
function assertBoolean(x: boolean) {
>assertBoolean : (x: boolean) => boolean
>x : boolean
return x;
>x : boolean
}
function assertString(x: string) {
>assertString : (x: string) => string
>x : string
return x;
>x : string
}
function assertSymbol(x: symbol) {
>assertSymbol : (x: symbol) => symbol
>x : symbol
return x;
>x : symbol
}
function assertFunction(x: Function) {
>assertFunction : (x: Function) => Function
>x : Function
>Function : Function
return x;
>x : Function
}
function assertObject(x: object) {
>assertObject : (x: object) => object
>x : object
return x;
>x : object
}
function assertUndefined(x: undefined) {
>assertUndefined : (x: undefined) => undefined
>x : undefined
return x;
>x : undefined
}
function assertAll(x: Basic) {
>assertAll : (x: Basic) => Basic
>x : Basic
>Basic : Basic
return x;
>x : Basic
}
type Basic = number | boolean | string | symbol | object | Function | undefined;
>Basic : Basic
>Function : Function
function testUnion(x: Basic) {
>testUnion : (x: Basic) => void
>x : Basic
>Basic : Basic
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : Basic
case 'number': assertNumber(x); return;
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : number
case 'boolean': assertBoolean(x); return;
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : boolean
case 'function': assertFunction(x); return;
>'function' : "function"
>assertFunction(x) : Function
>assertFunction : (x: Function) => Function
>x : Function
case 'symbol': assertSymbol(x); return;
>'symbol' : "symbol"
>assertSymbol(x) : symbol
>assertSymbol : (x: symbol) => symbol
>x : symbol
case 'object': assertObject(x); return;
>'object' : "object"
>assertObject(x) : object
>assertObject : (x: object) => object
>x : object
case 'string': assertString(x); return;
>'string' : "string"
>assertString(x) : string
>assertString : (x: string) => string
>x : string
case 'undefined': assertUndefined(x); return;
>'undefined' : "undefined"
>assertUndefined(x) : undefined
>assertUndefined : (x: undefined) => undefined
>x : undefined
}
assertNever(x);
>assertNever(x) : never
>assertNever : (x: never) => never
>x : never
}
function testExtendsUnion<T extends Basic>(x: T) {
>testExtendsUnion : <T extends Basic>(x: T) => void
>T : T
>Basic : Basic
>x : T
>T : T
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
case 'number': assertNumber(x); return;
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : T & number
case 'boolean': assertBoolean(x); return;
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : (T & true) | (T & false)
case 'function': assertAll(x); return;
>'function' : "function"
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : T
case 'symbol': assertSymbol(x); return;
>'symbol' : "symbol"
>assertSymbol(x) : symbol
>assertSymbol : (x: symbol) => symbol
>x : T & symbol
case 'object': assertAll(x); return;
>'object' : "object"
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : T
case 'string': assertString(x); return;
>'string' : "string"
>assertString(x) : string
>assertString : (x: string) => string
>x : T & string
case 'undefined': assertUndefined(x); return;
>'undefined' : "undefined"
>assertUndefined(x) : undefined
>assertUndefined : (x: undefined) => undefined
>x : T & undefined
}
assertAll(x);
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : T
}
function testAny(x: any) {
>testAny : (x: any) => void
>x : any
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : any
case 'number': assertNumber(x); return;
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : number
case 'boolean': assertBoolean(x); return;
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : boolean
case 'function': assertFunction(x); return;
>'function' : "function"
>assertFunction(x) : Function
>assertFunction : (x: Function) => Function
>x : any
case 'symbol': assertSymbol(x); return;
>'symbol' : "symbol"
>assertSymbol(x) : symbol
>assertSymbol : (x: symbol) => symbol
>x : symbol
case 'object': assertObject(x); return;
>'object' : "object"
>assertObject(x) : object
>assertObject : (x: object) => object
>x : any
case 'string': assertString(x); return;
>'string' : "string"
>assertString(x) : string
>assertString : (x: string) => string
>x : string
case 'undefined': assertUndefined(x); return;
>'undefined' : "undefined"
>assertUndefined(x) : undefined
>assertUndefined : (x: undefined) => undefined
>x : undefined
}
assertAll(x); // is any
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : any
}
function a1(x: string | object | undefined) {
>a1 : (x: string | object | undefined) => string | object | undefined
>x : string | object | undefined
return x;
>x : string | object | undefined
}
function testUnionExplicitDefault(x: Basic) {
>testUnionExplicitDefault : (x: Basic) => void
>x : Basic
>Basic : Basic
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : Basic
case 'number': assertNumber(x); return;
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : number
case 'boolean': assertBoolean(x); return;
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : boolean
case 'function': assertFunction(x); return;
>'function' : "function"
>assertFunction(x) : Function
>assertFunction : (x: Function) => Function
>x : Function
case 'symbol': assertSymbol(x); return;
>'symbol' : "symbol"
>assertSymbol(x) : symbol
>assertSymbol : (x: symbol) => symbol
>x : symbol
default: a1(x); return;
>a1(x) : string | object | undefined
>a1 : (x: string | object | undefined) => string | object | undefined
>x : string | object | undefined
}
}
function testUnionImplicitDefault(x: Basic) {
>testUnionImplicitDefault : (x: Basic) => string | object | undefined
>x : Basic
>Basic : Basic
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : Basic
case 'number': assertNumber(x); return;
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : number
case 'boolean': assertBoolean(x); return;
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : boolean
case 'function': assertFunction(x); return;
>'function' : "function"
>assertFunction(x) : Function
>assertFunction : (x: Function) => Function
>x : Function
case 'symbol': assertSymbol(x); return;
>'symbol' : "symbol"
>assertSymbol(x) : symbol
>assertSymbol : (x: symbol) => symbol
>x : symbol
}
return a1(x);
>a1(x) : string | object | undefined
>a1 : (x: string | object | undefined) => string | object | undefined
>x : string | object | undefined
}
function testExtendsExplicitDefault<T extends Basic>(x: T) {
>testExtendsExplicitDefault : <T extends Basic>(x: T) => void
>T : T
>Basic : Basic
>x : T
>T : T
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
case 'number': assertNumber(x); return;
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : T & number
case 'boolean': assertBoolean(x); return;
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : (T & true) | (T & false)
case 'function': assertAll(x); return;
>'function' : "function"
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : T
case 'symbol': assertSymbol(x); return;
>'symbol' : "symbol"
>assertSymbol(x) : symbol
>assertSymbol : (x: symbol) => symbol
>x : T & symbol
default: assertAll(x); return;
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : T
}
}
function testExtendsImplicitDefault<T extends Basic>(x: T) {
>testExtendsImplicitDefault : <T extends Basic>(x: T) => string | number | boolean | symbol | object | undefined
>T : T
>Basic : Basic
>x : T
>T : T
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
case 'number': assertNumber(x); return;
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : T & number
case 'boolean': assertBoolean(x); return;
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : (T & true) | (T & false)
case 'function': assertAll(x); return;
>'function' : "function"
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : T
case 'symbol': assertSymbol(x); return;
>'symbol' : "symbol"
>assertSymbol(x) : symbol
>assertSymbol : (x: symbol) => symbol
>x : T & symbol
}
return assertAll(x);
>assertAll(x) : Basic
>assertAll : (x: Basic) => Basic
>x : T
}
type L = (x: number) => string;
>L : L
>x : number
type R = { x: string, y: number }
>R : R
>x : string
>y : number
function exhaustiveChecks(x: number | string | L | R): string {
>exhaustiveChecks : (x: string | number | R | L) => string
>x : string | number | R | L
>L : L
>R : R
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : string | number | R | L
case 'number': return x.toString(2);
>'number' : "number"
>x.toString(2) : string
>x.toString : (radix?: number | undefined) => string
>x : number
>toString : (radix?: number | undefined) => string
>2 : 2
case 'string': return x;
>'string' : "string"
>x : string
case 'function': return x(42);
>'function' : "function"
>x(42) : string
>x : L
>42 : 42
case 'object': return x.x;
>'object' : "object"
>x.x : string
>x : R
>x : string
}
}
function exhaustiveChecksGenerics<T extends L | R | number | string>(x: T): string {
>exhaustiveChecksGenerics : <T extends string | number | R | L>(x: T) => string
>T : T
>L : L
>R : R
>x : T
>T : T
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
case 'number': return x.toString(2);
>'number' : "number"
>x.toString(2) : string
>x.toString : ((radix?: number | undefined) => string) | ((() => string) & ((radix?: number | undefined) => string)) | ((() => string) & ((radix?: number | undefined) => string)) | ((() => string) & ((radix?: number | undefined) => string))
>x : T & number
>toString : ((radix?: number | undefined) => string) | ((() => string) & ((radix?: number | undefined) => string)) | ((() => string) & ((radix?: number | undefined) => string)) | ((() => string) & ((radix?: number | undefined) => string))
>2 : 2
case 'string': return x;
>'string' : "string"
>x : T & string
case 'function': return (x as L)(42); // Can't narrow generic
>'function' : "function"
>(x as L)(42) : string
>(x as L) : L
>x as L : L
>x : T
>L : L
>42 : 42
case 'object': return (x as R).x; // Can't narrow generic
>'object' : "object"
>(x as R).x : string
>(x as R) : R
>x as R : R
>x : T
>R : R
>x : string
}
}
function multipleGeneric<X extends L, Y extends R>(xy: X | Y): [X, string] | [Y, number] {
>multipleGeneric : <X extends L, Y extends R>(xy: X | Y) => [X, string] | [Y, number]
>X : X
>L : L
>Y : Y
>R : R
>xy : X | Y
>X : X
>Y : Y
>X : X
>Y : Y
switch (typeof xy) {
>typeof xy : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>xy : X | Y
case 'function': return [xy, xy(42)];
>'function' : "function"
>[xy, xy(42)] : [X, string]
>xy : X
>xy(42) : string
>xy : X
>42 : 42
case 'object': return [xy, xy.y];
>'object' : "object"
>[xy, xy.y] : [Y, number]
>xy : Y
>xy.y : number
>xy : Y
>y : number
default: return assertNever(xy);
>assertNever(xy) : never
>assertNever : (x: never) => never
>xy : never
}
}
function multipleGenericFuse<X extends L | number, Y extends R | number>(xy: X | Y): [X, number] | [Y, string] | [(X | Y)] {
>multipleGenericFuse : <X extends number | L, Y extends number | R>(xy: X | Y) => [X, number] | [Y, string] | [X | Y]
>X : X
>L : L
>Y : Y
>R : R
>xy : X | Y
>X : X
>Y : Y
>X : X
>Y : Y
>X : X
>Y : Y
switch (typeof xy) {
>typeof xy : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>xy : X | Y
case 'function': return [xy, 1];
>'function' : "function"
>[xy, 1] : [X, number]
>xy : X
>1 : 1
case 'object': return [xy, 'two'];
>'object' : "object"
>[xy, 'two'] : [Y, string]
>xy : Y
>'two' : "two"
case 'number': return [xy]
>'number' : "number"
>[xy] : [X | Y]
>xy : X | Y
}
}
function multipleGenericExhaustive<X extends L, Y extends R>(xy: X | Y): [X, string] | [Y, number] {
>multipleGenericExhaustive : <X extends L, Y extends R>(xy: X | Y) => [X, string] | [Y, number]
>X : X
>L : L
>Y : Y
>R : R
>xy : X | Y
>X : X
>Y : Y
>X : X
>Y : Y
switch (typeof xy) {
>typeof xy : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>xy : X | Y
case 'object': return [xy, xy.y];
>'object' : "object"
>[xy, xy.y] : [Y, number]
>xy : Y
>xy.y : number
>xy : Y
>y : number
case 'function': return [xy, xy(42)];
>'function' : "function"
>[xy, xy(42)] : [X, string]
>xy : X
>xy(42) : string
>xy : X
>42 : 42
}
}
function switchOrdering(x: string | number | boolean) {
>switchOrdering : (x: string | number | boolean) => string | number | boolean
>x : string | number | boolean
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : string | number | boolean
case 'string': return assertString(x);
>'string' : "string"
>assertString(x) : string
>assertString : (x: string) => string
>x : string
case 'number': return assertNumber(x);
>'number' : "number"
>assertNumber(x) : number
>assertNumber : (x: number) => number
>x : number
case 'boolean': return assertBoolean(x);
>'boolean' : "boolean"
>assertBoolean(x) : boolean
>assertBoolean : (x: boolean) => boolean
>x : boolean
case 'number': return assertNever(x);
>'number' : "number"
>assertNever(x) : never
>assertNever : (x: never) => never
>x : never
}
}
function switchOrderingWithDefault(x: string | number | boolean) {
>switchOrderingWithDefault : (x: string | number | boolean) => string | number | boolean
>x : string | number | boolean
function local(y: string | number | boolean) {
>local : (y: string | number | boolean) => string | number | boolean
>y : string | number | boolean
return x;
>x : string | number | boolean
}
switch (typeof x) {
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : string | number | boolean
case 'string':
>'string' : "string"
case 'number':
>'number' : "number"
default: return local(x)
>local(x) : string | number | boolean
>local : (y: string | number | boolean) => string | number | boolean
>x : string | number | boolean
case 'string': return assertNever(x);
>'string' : "string"
>assertNever(x) : never
>assertNever : (x: never) => never
>x : never
case 'number': return assertNever(x);
>'number' : "number"
>assertNever(x) : never
>assertNever : (x: never) => never
>x : never
}
}

View File

@@ -0,0 +1,190 @@
// @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 assertUndefined(x: undefined) {
return x;
}
function assertAll(x: Basic) {
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);
}
}