From 0d79831ead51bda2fd38dd169e0bf04a0cdb4e72 Mon Sep 17 00:00:00 2001 From: Jack Williams <jw@jackw.io> Date: Tue, 13 Feb 2018 01:14:47 +0000 Subject: [PATCH 1/6] 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 --- src/compiler/binder.ts | 2 + src/compiler/checker.ts | 119 +++ .../reference/narrowingByTypeofInSwitch.js | 427 +++++++++++ .../narrowingByTypeofInSwitch.symbols | 542 ++++++++++++++ .../reference/narrowingByTypeofInSwitch.types | 695 ++++++++++++++++++ .../compiler/narrowingByTypeofInSwitch.ts | 190 +++++ 6 files changed, 1975 insertions(+) create mode 100644 tests/baselines/reference/narrowingByTypeofInSwitch.js create mode 100644 tests/baselines/reference/narrowingByTypeofInSwitch.symbols create mode 100644 tests/baselines/reference/narrowingByTypeofInSwitch.types create mode 100644 tests/cases/compiler/narrowingByTypeofInSwitch.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e6e3364840dc9..555c13f1f61e3 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -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; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f497ba82e47a8..ab2cc1f3b8519 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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; diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.js b/tests/baselines/reference/narrowingByTypeofInSwitch.js new file mode 100644 index 0000000000000..2cf4b9027c2c8 --- /dev/null +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.js @@ -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); + } +} diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.symbols b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols new file mode 100644 index 0000000000000..2d1bd06baba98 --- /dev/null +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols @@ -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)) + } +} + diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.types b/tests/baselines/reference/narrowingByTypeofInSwitch.types new file mode 100644 index 0000000000000..785bdd23609d5 --- /dev/null +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.types @@ -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 + } +} + diff --git a/tests/cases/compiler/narrowingByTypeofInSwitch.ts b/tests/cases/compiler/narrowingByTypeofInSwitch.ts new file mode 100644 index 0000000000000..aadd351b87e00 --- /dev/null +++ b/tests/cases/compiler/narrowingByTypeofInSwitch.ts @@ -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); + } +} From 9e43183884825fa3b17359ac167fdaa6aacb2530 Mon Sep 17 00:00:00 2001 From: Jack Williams <jw@jackw.io> Date: Wed, 23 May 2018 01:03:04 +0100 Subject: [PATCH 2/6] Add fall-through test and correct comment about implied type --- src/compiler/checker.ts | 19 +- .../reference/narrowingByTypeofInSwitch.js | 45 ++ .../narrowingByTypeofInSwitch.symbols | 415 ++++++++++-------- .../reference/narrowingByTypeofInSwitch.types | 62 +++ .../compiler/narrowingByTypeofInSwitch.ts | 24 + 5 files changed, 375 insertions(+), 190 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab2cc1f3b8519..1629717ff2861 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13594,10 +13594,10 @@ namespace ts { // } // // 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 second clause is never, but this does not get just because it includes a default case. // 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); + let impliedType = getTypeWithFacts(getUnionType((<string[]>clauseWitnesses).map(text => typeofTypesByName.get(text) || neverType)), switchFacts); if (impliedType.flags & TypeFlags.Union) { impliedType = getAssignmentReducedType(impliedType as UnionType, getBaseConstraintOfType(type) || type); } @@ -19027,17 +19027,20 @@ namespace ts { 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; + const witness = witnesses[i]; + facts |= (witness && typeofNEFacts.get(witness)) || 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); + const witness = witnesses[i]; + facts &= ~((witness && typeofNEFacts.get(witness)) || 0); } // Add inequalities for types before the active clause unconditionally. for (let i = 0; i < start; i++) { - facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject; + const witness = witnesses[i]; + facts |= (witness && typeofNEFacts.get(witness)) || TypeFacts.TypeofNEHostObject; } } // When in an active clause without default the set of @@ -19045,12 +19048,14 @@ namespace ts { else { // Add equalities for all types in the active clause. for (let i = start; i < end; i++) { - facts |= typeofEQFacts.get(witnesses[i]) || TypeFacts.TypeofEQHostObject; + const witness = witnesses[i]; + facts |= (witness && typeofEQFacts.get(witness)) || 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); + const witness = witnesses[i]; + facts &= ~((witness && typeofEQFacts.get(witness)) || 0); } } return facts; diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.js b/tests/baselines/reference/narrowingByTypeofInSwitch.js index 2cf4b9027c2c8..95ec120a85d2d 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.js +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.js @@ -35,6 +35,14 @@ 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) { @@ -186,6 +194,22 @@ function switchOrderingWithDefault(x: string | number | boolean) { 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; + } +} //// [narrowingByTypeofInSwitch.js] @@ -216,6 +240,12 @@ function assertUndefined(x) { function assertAll(x) { return x; } +function assertStringOrNumber(x) { + return x; +} +function assertBooleanOrObject(x) { + return x; +} function testUnion(x) { switch (typeof x) { case 'number': @@ -425,3 +455,18 @@ function switchOrderingWithDefault(x) { case 'number': return assertNever(x); } } +function fallThroughTest(x) { + switch (typeof x) { + case 'number': + assertNumber(x); + case 'string': + assertStringOrNumber(x); + break; + default: + assertObject(x); + case 'number': + case 'boolean': + assertBooleanOrObject(x); + break; + } +} diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.symbols b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols index 2d1bd06baba98..b451b39ac7c22 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.symbols +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols @@ -67,476 +67,525 @@ function assertUndefined(x: undefined) { 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)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) return x; >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 32, 19)) } +function assertStringOrNumber(x: string | number) { +>assertStringOrNumber : Symbol(assertStringOrNumber, Decl(narrowingByTypeofInSwitch.ts, 34, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 36, 30)) + + return x; +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 36, 30)) +} + +function assertBooleanOrObject(x: boolean | object) { +>assertBooleanOrObject : Symbol(assertBooleanOrObject, Decl(narrowingByTypeofInSwitch.ts, 38, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 40, 31)) + + return x; +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 40, 31)) +} + type Basic = number | boolean | string | symbol | object | Function | undefined; ->Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 34, 1)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 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)) +>testUnion : Symbol(testUnion, Decl(narrowingByTypeofInSwitch.ts, 44, 80)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) case 'number': assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) case 'boolean': assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) case 'function': assertFunction(x); return; >assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) case 'symbol': assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) case 'object': assertObject(x); return; >assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) case 'string': assertString(x); return; >assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) case 'undefined': assertUndefined(x); return; >assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 26, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 19)) } assertNever(x); >assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 38, 19)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 46, 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)) +>testExtendsUnion : Symbol(testExtendsUnion, Decl(narrowingByTypeofInSwitch.ts, 57, 1)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 59, 26)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 59, 26)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) case 'number': assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) case 'boolean': assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) case 'function': assertAll(x); return; >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) case 'symbol': assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) case 'object': assertAll(x); return; >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) case 'string': assertString(x); return; >assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) case 'undefined': assertUndefined(x); return; >assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 26, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) } assertAll(x); >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 51, 43)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 59, 43)) } function testAny(x: any) { ->testAny : Symbol(testAny, Decl(narrowingByTypeofInSwitch.ts, 62, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>testAny : Symbol(testAny, Decl(narrowingByTypeofInSwitch.ts, 70, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) case 'number': assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) case 'boolean': assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) case 'function': assertFunction(x); return; >assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) case 'symbol': assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) case 'object': assertObject(x); return; >assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) case 'string': assertString(x); return; >assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) case 'undefined': assertUndefined(x); return; >assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 26, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) } assertAll(x); // is any >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 64, 17)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 72, 17)) } function a1(x: string | object | undefined) { ->a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 75, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 77, 12)) +>a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 83, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 85, 12)) return x; ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 77, 12)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 85, 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)) +>testUnionExplicitDefault : Symbol(testUnionExplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 87, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 89, 34)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 89, 34)) case 'number': assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 89, 34)) case 'boolean': assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 89, 34)) case 'function': assertFunction(x); return; >assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 89, 34)) case 'symbol': assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 89, 34)) default: a1(x); return; ->a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 75, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 81, 34)) +>a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 83, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 89, 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)) +>testUnionImplicitDefault : Symbol(testUnionImplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 97, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 99, 34)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 99, 34)) case 'number': assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 99, 34)) case 'boolean': assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 99, 34)) case 'function': assertFunction(x); return; >assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 99, 34)) case 'symbol': assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 99, 34)) } return a1(x); ->a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 75, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 91, 34)) +>a1 : Symbol(a1, Decl(narrowingByTypeofInSwitch.ts, 83, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 99, 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)) +>testExtendsExplicitDefault : Symbol(testExtendsExplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 107, 1)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 109, 36)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 109, 53)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 109, 36)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 109, 53)) case 'number': assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 109, 53)) case 'boolean': assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 109, 53)) case 'function': assertAll(x); return; >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 109, 53)) case 'symbol': assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 109, 53)) default: assertAll(x); return; >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 101, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 109, 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)) +>testExtendsImplicitDefault : Symbol(testExtendsImplicitDefault, Decl(narrowingByTypeofInSwitch.ts, 118, 1)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 120, 36)) +>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 120, 53)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 120, 36)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 120, 53)) case 'number': assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 120, 53)) case 'boolean': assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 120, 53)) case 'function': assertAll(x); return; >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 120, 53)) case 'symbol': assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 120, 53)) } return assertAll(x); >assertAll : Symbol(assertAll, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 112, 53)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 120, 53)) } type L = (x: number) => string; ->L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 120, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 122, 10)) +>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 128, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 130, 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)) +>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 130, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 131, 10)) +>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 131, 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)) +>exhaustiveChecks : Symbol(exhaustiveChecks, Decl(narrowingByTypeofInSwitch.ts, 131, 33)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) +>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 128, 1)) +>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 130, 31)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) case 'number': return x.toString(2); >x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) >toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) case 'string': return x; ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) case 'function': return x(42); ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 125, 26)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 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)) +>x.x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 131, 10)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 131, 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)) +>exhaustiveChecksGenerics : Symbol(exhaustiveChecksGenerics, Decl(narrowingByTypeofInSwitch.ts, 140, 1)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 142, 34)) +>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 128, 1)) +>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 130, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 69)) +>T : Symbol(T, Decl(narrowingByTypeofInSwitch.ts, 142, 34)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 134, 69)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 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)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 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)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 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)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 69)) +>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 128, 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)) +>(x as R).x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 131, 10)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 69)) +>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 130, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 131, 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)) +>multipleGeneric : Symbol(multipleGeneric, Decl(narrowingByTypeofInSwitch.ts, 149, 1)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 151, 25)) +>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 128, 1)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 151, 37)) +>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 130, 31)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 51)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 151, 25)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 151, 37)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 151, 25)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 151, 37)) switch (typeof xy) { ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 51)) case 'function': return [xy, xy(42)]; ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51)) ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 51)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 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)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 51)) +>xy.y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 131, 21)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 51)) +>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 131, 21)) default: return assertNever(xy); >assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 143, 51)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 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)) +>multipleGenericFuse : Symbol(multipleGenericFuse, Decl(narrowingByTypeofInSwitch.ts, 157, 1)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 159, 29)) +>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 128, 1)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 159, 50)) +>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 130, 31)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 73)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 159, 29)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 159, 50)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 159, 29)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 159, 50)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 159, 29)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 159, 50)) switch (typeof xy) { ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 73)) case 'function': return [xy, 1]; ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 73)) case 'object': return [xy, 'two']; ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 73)) case 'number': return [xy] ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 151, 73)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 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)) +>multipleGenericExhaustive : Symbol(multipleGenericExhaustive, Decl(narrowingByTypeofInSwitch.ts, 165, 1)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 167, 35)) +>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 128, 1)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 167, 47)) +>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 130, 31)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 167, 61)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 167, 35)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 167, 47)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 167, 35)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 167, 47)) switch (typeof xy) { ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 167, 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)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 167, 61)) +>xy.y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 131, 21)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 167, 61)) +>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 131, 21)) case 'function': return [xy, xy(42)]; ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61)) ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 159, 61)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 167, 61)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 167, 61)) } } function switchOrdering(x: string | number | boolean) { ->switchOrdering : Symbol(switchOrdering, Decl(narrowingByTypeofInSwitch.ts, 164, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24)) +>switchOrdering : Symbol(switchOrdering, Decl(narrowingByTypeofInSwitch.ts, 172, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 174, 24)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 174, 24)) case 'string': return assertString(x); >assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 174, 24)) case 'number': return assertNumber(x); >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 174, 24)) case 'boolean': return assertBoolean(x); >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 174, 24)) case 'number': return assertNever(x); >assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 166, 24)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 174, 24)) } } function switchOrderingWithDefault(x: string | number | boolean) { ->switchOrderingWithDefault : Symbol(switchOrderingWithDefault, Decl(narrowingByTypeofInSwitch.ts, 173, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35)) +>switchOrderingWithDefault : Symbol(switchOrderingWithDefault, Decl(narrowingByTypeofInSwitch.ts, 181, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 183, 35)) function local(y: string | number | boolean) { ->local : Symbol(local, Decl(narrowingByTypeofInSwitch.ts, 175, 66)) ->y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 176, 19)) +>local : Symbol(local, Decl(narrowingByTypeofInSwitch.ts, 183, 66)) +>y : Symbol(y, Decl(narrowingByTypeofInSwitch.ts, 184, 19)) return x; ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 183, 35)) } switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 183, 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)) +>local : Symbol(local, Decl(narrowingByTypeofInSwitch.ts, 183, 66)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 183, 35)) case 'string': return assertNever(x); >assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 183, 35)) case 'number': return assertNever(x); >assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 175, 35)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 183, 35)) + } +} + +function fallThroughTest(x: string | number | boolean | object) { +>fallThroughTest : Symbol(fallThroughTest, Decl(narrowingByTypeofInSwitch.ts, 194, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 196, 25)) + + switch (typeof x) { +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 196, 25)) + + case 'number': + assertNumber(x) +>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 196, 25)) + + case 'string': + assertStringOrNumber(x) +>assertStringOrNumber : Symbol(assertStringOrNumber, Decl(narrowingByTypeofInSwitch.ts, 34, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 196, 25)) + + break; + default: + assertObject(x); +>assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 196, 25)) + + case 'number': + case 'boolean': + assertBooleanOrObject(x); +>assertBooleanOrObject : Symbol(assertBooleanOrObject, Decl(narrowingByTypeofInSwitch.ts, 38, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 196, 25)) + + break; } } diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.types b/tests/baselines/reference/narrowingByTypeofInSwitch.types index 785bdd23609d5..8672f4c9e7d09 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.types +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.types @@ -73,6 +73,22 @@ function assertAll(x: Basic) { >x : Basic } +function assertStringOrNumber(x: string | number) { +>assertStringOrNumber : (x: string | number) => string | number +>x : string | number + + return x; +>x : string | number +} + +function assertBooleanOrObject(x: boolean | object) { +>assertBooleanOrObject : (x: boolean | object) => boolean | object +>x : boolean | object + + return x; +>x : boolean | object +} + type Basic = number | boolean | string | symbol | object | Function | undefined; >Basic : Basic >Function : Function @@ -693,3 +709,49 @@ function switchOrderingWithDefault(x: string | number | boolean) { } } +function fallThroughTest(x: string | number | boolean | object) { +>fallThroughTest : (x: string | number | boolean | object) => void +>x : string | number | boolean | object + + switch (typeof x) { +>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : string | number | boolean | object + + case 'number': +>'number' : "number" + + assertNumber(x) +>assertNumber(x) : number +>assertNumber : (x: number) => number +>x : number + + case 'string': +>'string' : "string" + + assertStringOrNumber(x) +>assertStringOrNumber(x) : string | number +>assertStringOrNumber : (x: string | number) => string | number +>x : string | number + + break; + default: + assertObject(x); +>assertObject(x) : object +>assertObject : (x: object) => object +>x : object + + case 'number': +>'number' : "number" + + case 'boolean': +>'boolean' : "boolean" + + assertBooleanOrObject(x); +>assertBooleanOrObject(x) : boolean | object +>assertBooleanOrObject : (x: boolean | object) => boolean | object +>x : boolean | object + + break; + } +} + diff --git a/tests/cases/compiler/narrowingByTypeofInSwitch.ts b/tests/cases/compiler/narrowingByTypeofInSwitch.ts index aadd351b87e00..252c1d9445ac9 100644 --- a/tests/cases/compiler/narrowingByTypeofInSwitch.ts +++ b/tests/cases/compiler/narrowingByTypeofInSwitch.ts @@ -37,6 +37,14 @@ 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) { @@ -188,3 +196,19 @@ function switchOrderingWithDefault(x: string | number | boolean) { 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; + } +} From 6391742dca16cdddbf5a9fd5a5a42c216a80f92f Mon Sep 17 00:00:00 2001 From: Jack Williams <jw@jackw.io> Date: Wed, 23 May 2018 03:12:50 +0100 Subject: [PATCH 3/6] Make undefined for default case less pervasive by removing once done with it --- src/compiler/checker.ts | 50 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1629717ff2861..25a9039009e56 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13572,10 +13572,27 @@ namespace ts { 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); + const defaultCaseLocation = findIndex(switchWitnesses, elem => elem === undefined); + const hasDefaultClause = clauseStart === clauseEnd || (defaultCaseLocation >= clauseStart && defaultCaseLocation < clauseEnd); + let clauseWitnesses: string[]; + let switchFacts: TypeFacts; + if (defaultCaseLocation > -1) { + // We no longer need the undefined denoting an + // explicit default case. Remove the undefined and + // fix-up clauseStart and clauseEnd. This means + // that we don't have to worry about undefined + // in the witness array. + const witnesses = <string[]>switchWitnesses.filter(witness => witness !== undefined); + const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart; + const fixedClauseEnd = defaultCaseLocation < clauseEnd ? clauseEnd - 1 : clauseEnd; + clauseWitnesses = witnesses.slice(fixedClauseStart, fixedClauseEnd); + switchFacts = getFactsFromTypeofSwitch(fixedClauseStart, fixedClauseEnd, witnesses, hasDefaultClause); + } + else { + clauseWitnesses = <string[]>switchWitnesses.slice(clauseStart, clauseEnd); + switchFacts = getFactsFromTypeofSwitch(clauseStart, clauseEnd, <string[]>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. @@ -13594,10 +13611,10 @@ namespace ts { // } // // The implied type of the first clause number | string. - // The implied type of the second clause is never, but this does not get just because it includes a default case. + // The implied type of the second clause is never, but this does not get used because it includes a default case. // The implied type of the third clause is boolean (number has already be caught). if (!(hasDefaultClause || (type.flags & TypeFlags.Union))) { - let impliedType = getTypeWithFacts(getUnionType((<string[]>clauseWitnesses).map(text => typeofTypesByName.get(text) || neverType)), switchFacts); + 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); } @@ -19019,7 +19036,7 @@ namespace ts { * 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 { + function getFactsFromTypeofSwitch(start: number, end: number, witnesses: string[], 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 @@ -19027,20 +19044,17 @@ namespace ts { if (hasDefault) { // Value is not equal to any types after the active clause. for (let i = end; i < witnesses.length; i++) { - const witness = witnesses[i]; - facts |= (witness && typeofNEFacts.get(witness)) || TypeFacts.TypeofNEHostObject; + 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++) { - const witness = witnesses[i]; - facts &= ~((witness && typeofNEFacts.get(witness)) || 0); + facts &= ~(typeofNEFacts.get(witnesses[i]) || 0); } // Add inequalities for types before the active clause unconditionally. for (let i = 0; i < start; i++) { - const witness = witnesses[i]; - facts |= (witness && typeofNEFacts.get(witness)) || TypeFacts.TypeofNEHostObject; + facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject; } } // When in an active clause without default the set of @@ -19048,14 +19062,12 @@ namespace ts { else { // Add equalities for all types in the active clause. for (let i = start; i < end; i++) { - const witness = witnesses[i]; - facts |= (witness && typeofEQFacts.get(witness)) || TypeFacts.TypeofEQHostObject; + facts |= typeofEQFacts.get(witnesses[i]) || TypeFacts.TypeofEQHostObject; } // Remove equalities for types that appear before the // active clause. for (let i = 0; i < start; i++) { - const witness = witnesses[i]; - facts &= ~((witness && typeofEQFacts.get(witness)) || 0); + facts &= ~(typeofEQFacts.get(witnesses[i]) || 0); } } return facts; @@ -19067,8 +19079,10 @@ namespace ts { } 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); + // This cast is safe because the switch is possibly exhaustive and does not contain a default case, so there can be no undefined. + const witnesses = <string[]>getSwitchClauseTypeOfWitnesses(node); + // notEqualFacts states that the type of the switched value is not equal to every type in the switch. + const notEqualFacts = getFactsFromTypeofSwitch(0, 0, witnesses, /*hasDefault*/ true); const type = getBaseConstraintOfType(operandType) || operandType; return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never); } From 4d8529c9eb6c1cab39eabe4636525d4e79cfdc70 Mon Sep 17 00:00:00 2001 From: Jack Williams <jw@jackw.io> Date: Wed, 11 Jul 2018 11:00:44 +0100 Subject: [PATCH 4/6] Improve comments in narrowBySwitchOnTypeOf --- src/compiler/checker.ts | 51 +++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 25a9039009e56..539430c264253 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12840,6 +12840,8 @@ namespace ts { return links.switchTypes; } + // Get the types from all cases in a switch on `typeof`. An + // `undefined` element denotes an explicit `default` clause. function getSwitchClauseTypeOfWitnesses(switchStatement: SwitchStatement): (string | undefined)[] { const witnesses: (string | undefined)[] = []; for (const clause of switchStatement.caseBlock.clauses) { @@ -13584,6 +13586,7 @@ namespace ts { // that we don't have to worry about undefined // in the witness array. const witnesses = <string[]>switchWitnesses.filter(witness => witness !== undefined); + // The adjust clause start and end after removing the `default` statement. const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart; const fixedClauseEnd = defaultCaseLocation < clauseEnd ? clauseEnd - 1 : clauseEnd; clauseWitnesses = witnesses.slice(fixedClauseStart, fixedClauseEnd); @@ -13593,26 +13596,34 @@ namespace ts { clauseWitnesses = <string[]>switchWitnesses.slice(clauseStart, clauseEnd); switchFacts = getFactsFromTypeofSwitch(clauseStart, clauseEnd, <string[]>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 never, but this does not get used because it includes a default case. - // The implied type of the third clause is boolean (number has already be caught). + /* + The implied type is the raw type suggested by a + value being caught in this clause. + + When the clause contains a default case we ignore + the implied type and try to narrow using any facts + we can learn: see `switchFacts`. + + Example: + switch (typeof x) { + case 'number': + case 'string': break; + default: break; + case 'number': + case 'boolean': break + } + + In the first clause (case `number` and `string`) the + implied type is number | string. + + In the default clause we de not compute an implied type. + + In the third clause (case `number` and `boolean`) + the naive implied type is number | boolean, however + we use the type facts to narrow the implied type to + boolean. We know that number cannot be selected + because it is caught in the first clause. + */ if (!(hasDefaultClause || (type.flags & TypeFlags.Union))) { let impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => typeofTypesByName.get(text) || neverType)), switchFacts); if (impliedType.flags & TypeFlags.Union) { From 5aaf1e6b7a13955bcaa67413715581bd53c88d52 Mon Sep 17 00:00:00 2001 From: Jack Williams <jw@jackw.io> Date: Wed, 11 Jul 2018 17:45:22 +0100 Subject: [PATCH 5/6] Accept new baselines --- .../reference/narrowingByTypeofInSwitch.symbols | 12 ++++++------ .../reference/narrowingByTypeofInSwitch.types | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.symbols b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols index b451b39ac7c22..8024625181509 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.symbols +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols @@ -42,7 +42,7 @@ function assertSymbol(x: symbol) { 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, --, --)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) return x; >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 20, 24)) @@ -91,7 +91,7 @@ function assertBooleanOrObject(x: boolean | object) { type Basic = number | boolean | string | symbol | object | Function | undefined; >Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) ->Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) function testUnion(x: Basic) { >testUnion : Symbol(testUnion, Decl(narrowingByTypeofInSwitch.ts, 44, 80)) @@ -367,9 +367,9 @@ function exhaustiveChecks(x: number | string | L | R): string { >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) case 'number': return x.toString(2); ->x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) ->toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) case 'string': return x; >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 133, 26)) @@ -396,9 +396,9 @@ function exhaustiveChecksGenerics<T extends L | R | number | string>(x: T): stri >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 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.toString : Symbol(toString, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 2 more) >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 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) +>toString : Symbol(toString, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 2 more) case 'string': return x; >x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 142, 69)) diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.types b/tests/baselines/reference/narrowingByTypeofInSwitch.types index 8672f4c9e7d09..a2aef98feb4b6 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.types +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.types @@ -171,7 +171,7 @@ function testExtendsUnion<T extends Basic>(x: T) { >'boolean' : "boolean" >assertBoolean(x) : boolean >assertBoolean : (x: boolean) => boolean ->x : (T & true) | (T & false) +>x : (T & false) | (T & true) case 'function': assertAll(x); return; >'function' : "function" @@ -373,7 +373,7 @@ function testExtendsExplicitDefault<T extends Basic>(x: T) { >'boolean' : "boolean" >assertBoolean(x) : boolean >assertBoolean : (x: boolean) => boolean ->x : (T & true) | (T & false) +>x : (T & false) | (T & true) case 'function': assertAll(x); return; >'function' : "function" @@ -416,7 +416,7 @@ function testExtendsImplicitDefault<T extends Basic>(x: T) { >'boolean' : "boolean" >assertBoolean(x) : boolean >assertBoolean : (x: boolean) => boolean ->x : (T & true) | (T & false) +>x : (T & false) | (T & true) case 'function': assertAll(x); return; >'function' : "function" From 3173cfee97529ea9b4e5a93df33b243c108c7609 Mon Sep 17 00:00:00 2001 From: Jack Williams <jw@jackw.io> Date: Thu, 6 Sep 2018 09:45:22 +0100 Subject: [PATCH 6/6] Update narrowing baseline --- .../reference/narrowingByTypeofInSwitch.types | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.types b/tests/baselines/reference/narrowingByTypeofInSwitch.types index a2aef98feb4b6..000eea75f7908 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.types +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.types @@ -42,7 +42,6 @@ function assertSymbol(x: symbol) { function assertFunction(x: Function) { >assertFunction : (x: Function) => Function >x : Function ->Function : Function return x; >x : Function @@ -67,7 +66,6 @@ function assertUndefined(x: undefined) { function assertAll(x: Basic) { >assertAll : (x: Basic) => Basic >x : Basic ->Basic : Basic return x; >x : Basic @@ -91,12 +89,10 @@ function assertBooleanOrObject(x: boolean | object) { 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" @@ -152,10 +148,7 @@ function testUnion(x: Basic) { 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" @@ -276,7 +269,6 @@ function a1(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" @@ -316,7 +308,6 @@ function testUnionExplicitDefault(x: Basic) { 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" @@ -354,10 +345,7 @@ function testUnionImplicitDefault(x: Basic) { 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" @@ -397,10 +385,7 @@ function testExtendsExplicitDefault<T extends 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" @@ -448,8 +433,6 @@ type 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" @@ -483,11 +466,7 @@ function exhaustiveChecks(x: number | string | L | R): 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" @@ -511,7 +490,6 @@ function exhaustiveChecksGenerics<T extends L | R | number | string>(x: T): stri >(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 @@ -520,22 +498,13 @@ function exhaustiveChecksGenerics<T extends L | R | number | string>(x: T): stri >(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" @@ -566,17 +535,7 @@ function multipleGeneric<X extends L, Y extends R>(xy: X | Y): [X, string] | [Y, 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" @@ -603,15 +562,7 @@ function multipleGenericFuse<X extends L | number, Y extends R | number>(xy: X | 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"