diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 474100799b9a2..047cda46631ea 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20804,29 +20804,8 @@ namespace ts { const facts = assumeTrue ? typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject : typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject; - return getTypeWithFacts(assumeTrue ? mapType(type, narrowTypeForTypeof) : type, facts); - - function narrowTypeForTypeof(type: Type) { - // We narrow a non-union type to an exact primitive type if the non-union type - // is a supertype of that primitive type. For example, type 'any' can be narrowed - // to one of the primitive types. - const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text); - if (targetType) { - if (isTypeSubtypeOf(type, targetType)) { - return type; - } - if (isTypeSubtypeOf(targetType, type)) { - return targetType; - } - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type) || anyType; - if (isTypeSubtypeOf(targetType, constraint)) { - return getIntersectionType([type, targetType]); - } - } - } - return type; - } + const impliedType = getImpliedTypeFromTypeofGuard(type, literal.text); + return getTypeWithFacts(assumeTrue && impliedType ? mapType(type, narrowUnionMemberByTypeof(impliedType)) : type, facts); } function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) { @@ -20877,19 +20856,28 @@ namespace ts { return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]); } - function getImpliedTypeFromTypeofCase(type: Type, text: string) { + function getImpliedTypeFromTypeofGuard(type: Type, text: string) { switch (text) { case "function": return type.flags & TypeFlags.Any ? type : globalFunctionType; case "object": return type.flags & TypeFlags.Unknown ? getUnionType([nonPrimitiveType, nullType]) : type; default: - return typeofTypesByName.get(text) || type; + return typeofTypesByName.get(text); } } - function narrowTypeForTypeofSwitch(candidate: Type) { + // When narrowing a union type by a `typeof` guard using type-facts alone, constituent types that are + // super-types of the implied guard will be retained in the final type: this is because type-facts only + // filter. Instead, we would like to replace those union constituents with the more precise type implied by + // the guard. For example: narrowing `{} | undefined` by `"boolean"` should produce the type `boolean`, not + // the filtered type `{}`. For this reason we narrow constituents of the union individually, in addition to + // filtering by type-facts. + function narrowUnionMemberByTypeof(candidate: Type) { return (type: Type) => { + if (isTypeSubtypeOf(type, candidate)) { + return type; + } if (isTypeSubtypeOf(candidate, type)) { return candidate; } @@ -20914,11 +20902,9 @@ namespace ts { 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. + // 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 = switchWitnesses.filter(witness => witness !== undefined); // The adjusted clause start and end after removing the `default` statement. const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart; @@ -20961,11 +20947,8 @@ namespace ts { boolean. We know that number cannot be selected because it is caught in the first clause. */ - let impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofCase(type, text))), switchFacts); - if (impliedType.flags & TypeFlags.Union) { - impliedType = getAssignmentReducedType(impliedType as UnionType, getBaseConstraintOrType(type)); - } - return getTypeWithFacts(mapType(type, narrowTypeForTypeofSwitch(impliedType)), switchFacts); + const impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofGuard(type, text) || type)), switchFacts); + return getTypeWithFacts(mapType(type, narrowUnionMemberByTypeof(impliedType)), switchFacts); } function isMatchingConstructorReference(expr: Expression) { diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.js b/tests/baselines/reference/narrowingByTypeofInSwitch.js index f2b4d26c56e54..234829a4b5aed 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.js +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.js @@ -250,6 +250,20 @@ function narrowingNarrows(x: {} | undefined) { } } +function narrowingNarrows2(x: true | 3 | 'hello' | undefined) { + switch (typeof x) { + case 'number': assertNumber(x); return; + case 'boolean': assertBoolean(x); return; + case 'function': assertNever(x); return; + case 'symbol': assertNever(x); return; + case 'object': const _: {} = assertNever(x); return; + case 'string': assertString(x); return; + case 'undefined': assertUndefined(x); return; + case 'number': assertNever(x); return; + default: const _y: {} = assertNever(x); return; + } +} + /* Template literals */ function testUnionWithTempalte(x: Basic) { @@ -298,9 +312,11 @@ function multipleGenericFuseWithBoth case 'object': return [xy, 'two']; case `number`: return [xy] } -} +} + //// [narrowingByTypeofInSwitch.js] +"use strict"; function assertNever(x) { return x; } @@ -634,6 +650,37 @@ function narrowingNarrows(x) { return; } } +function narrowingNarrows2(x) { + switch (typeof x) { + case 'number': + assertNumber(x); + return; + case 'boolean': + assertBoolean(x); + return; + case 'function': + assertNever(x); + return; + case 'symbol': + assertNever(x); + return; + case 'object': + var _ = assertNever(x); + return; + case 'string': + assertString(x); + return; + case 'undefined': + assertUndefined(x); + return; + case 'number': + assertNever(x); + return; + default: + var _y = assertNever(x); + return; + } +} /* Template literals */ function testUnionWithTempalte(x) { switch (typeof x) { diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.symbols b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols index 052bacf17a1c6..ea1282393fca1 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.symbols +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.symbols @@ -715,144 +715,192 @@ function narrowingNarrows(x: {} | undefined) { } } +function narrowingNarrows2(x: true | 3 | 'hello' | undefined) { +>narrowingNarrows2 : Symbol(narrowingNarrows2, Decl(narrowingByTypeofInSwitch.ts, 249, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + switch (typeof x) { +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'number': assertNumber(x); return; +>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'boolean': assertBoolean(x); return; +>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'function': assertNever(x); return; +>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'symbol': assertNever(x); return; +>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'object': const _: {} = assertNever(x); return; +>_ : Symbol(_, Decl(narrowingByTypeofInSwitch.ts, 257, 28)) +>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'string': assertString(x); return; +>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'undefined': assertUndefined(x); return; +>assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + case 'number': assertNever(x); return; +>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + + default: const _y: {} = assertNever(x); return; +>_y : Symbol(_y, Decl(narrowingByTypeofInSwitch.ts, 261, 22)) +>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 251, 27)) + } +} + /* Template literals */ function testUnionWithTempalte(x: Basic) { ->testUnionWithTempalte : Symbol(testUnionWithTempalte, Decl(narrowingByTypeofInSwitch.ts, 249, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>testUnionWithTempalte : Symbol(testUnionWithTempalte, Decl(narrowingByTypeofInSwitch.ts, 263, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) >Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 46, 1)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) case `number`: assertNumber(x); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) case `boolean`: assertBoolean(x); return; >assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) case `function`: assertFunction(x); return; >assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) case `symbol`: assertSymbol(x); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) case `object`: assertObject(x); return; >assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) case `string`: assertString(x); return; >assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) case `undefined`: assertUndefined(x); return; >assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 30, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) } assertNever(x); >assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 267, 31)) } function fallThroughTestWithTempalte(x: string | number | boolean | object) { ->fallThroughTestWithTempalte : Symbol(fallThroughTestWithTempalte, Decl(narrowingByTypeofInSwitch.ts, 264, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37)) +>fallThroughTestWithTempalte : Symbol(fallThroughTestWithTempalte, Decl(narrowingByTypeofInSwitch.ts, 278, 1)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 280, 37)) switch (typeof x) { ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 280, 37)) case `number`: assertNumber(x) >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 280, 37)) case `string`: assertStringOrNumber(x) >assertStringOrNumber : Symbol(assertStringOrNumber, Decl(narrowingByTypeofInSwitch.ts, 38, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 280, 37)) break; default: assertObject(x); >assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 280, 37)) case `number`: case `boolean`: assertBooleanOrObject(x); >assertBooleanOrObject : Symbol(assertBooleanOrObject, Decl(narrowingByTypeofInSwitch.ts, 42, 1)) ->x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37)) +>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 280, 37)) break; } } function keyofNarrowingWithTemplate(k: keyof S) { ->keyofNarrowingWithTemplate : Symbol(keyofNarrowingWithTemplate, Decl(narrowingByTypeofInSwitch.ts, 280, 1)) ->S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36)) ->K : Symbol(K, Decl(narrowingByTypeofInSwitch.ts, 282, 49)) ->S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36)) ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) ->S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36)) +>keyofNarrowingWithTemplate : Symbol(keyofNarrowingWithTemplate, Decl(narrowingByTypeofInSwitch.ts, 294, 1)) +>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 296, 36)) +>K : Symbol(K, Decl(narrowingByTypeofInSwitch.ts, 296, 49)) +>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 296, 36)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) +>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 296, 36)) function assertKeyofS(k1: keyof S) { } ->assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87)) ->k1 : Symbol(k1, Decl(narrowingByTypeofInSwitch.ts, 283, 26)) ->S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36)) +>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 296, 87)) +>k1 : Symbol(k1, Decl(narrowingByTypeofInSwitch.ts, 297, 26)) +>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 296, 36)) switch (typeof k) { ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) case `number`: assertNumber(k); assertKeyofS(k); return; >assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1)) ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) ->assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87)) ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) +>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 296, 87)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) case `symbol`: assertSymbol(k); assertKeyofS(k); return; >assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1)) ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) ->assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87)) ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) +>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 296, 87)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) case `string`: assertString(k); assertKeyofS(k); return; >assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1)) ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) ->assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87)) ->k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) +>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 296, 87)) +>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 296, 74)) } } /* Both string literals and template literals */ function multipleGenericFuseWithBoth(xy: X | Y): [X, number] | [Y, string] | [(X | Y)] { ->multipleGenericFuseWithBoth : Symbol(multipleGenericFuseWithBoth, Decl(narrowingByTypeofInSwitch.ts, 289, 1)) ->X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37)) +>multipleGenericFuseWithBoth : Symbol(multipleGenericFuseWithBoth, Decl(narrowingByTypeofInSwitch.ts, 303, 1)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 307, 37)) >L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 132, 1)) ->Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 307, 58)) >R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 134, 31)) ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81)) ->X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37)) ->Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58)) ->X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37)) ->Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58)) ->X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37)) ->Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 307, 81)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 307, 37)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 307, 58)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 307, 37)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 307, 58)) +>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 307, 37)) +>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 307, 58)) switch (typeof xy) { ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 307, 81)) case `function`: return [xy, 1]; ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 307, 81)) case 'object': return [xy, 'two']; ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 307, 81)) case `number`: return [xy] ->xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81)) +>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 307, 81)) } } + diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.types b/tests/baselines/reference/narrowingByTypeofInSwitch.types index 90126810527f8..570f77e7a872f 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.types +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.types @@ -869,6 +869,72 @@ function narrowingNarrows(x: {} | undefined) { } } +function narrowingNarrows2(x: true | 3 | 'hello' | undefined) { +>narrowingNarrows2 : (x: true | 3 | 'hello' | undefined) => void +>x : true | 3 | "hello" | undefined +>true : true + + switch (typeof x) { +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : true | 3 | "hello" | undefined + + case 'number': assertNumber(x); return; +>'number' : "number" +>assertNumber(x) : number +>assertNumber : (x: number) => number +>x : 3 + + case 'boolean': assertBoolean(x); return; +>'boolean' : "boolean" +>assertBoolean(x) : boolean +>assertBoolean : (x: boolean) => boolean +>x : true + + case 'function': assertNever(x); return; +>'function' : "function" +>assertNever(x) : never +>assertNever : (x: never) => never +>x : never + + case 'symbol': assertNever(x); return; +>'symbol' : "symbol" +>assertNever(x) : never +>assertNever : (x: never) => never +>x : never + + case 'object': const _: {} = assertNever(x); return; +>'object' : "object" +>_ : {} +>assertNever(x) : never +>assertNever : (x: never) => never +>x : never + + case 'string': assertString(x); return; +>'string' : "string" +>assertString(x) : string +>assertString : (x: string) => string +>x : "hello" + + case 'undefined': assertUndefined(x); return; +>'undefined' : "undefined" +>assertUndefined(x) : undefined +>assertUndefined : (x: undefined) => undefined +>x : undefined + + case 'number': assertNever(x); return; +>'number' : "number" +>assertNever(x) : never +>assertNever : (x: never) => never +>x : never + + default: const _y: {} = assertNever(x); return; +>_y : {} +>assertNever(x) : never +>assertNever : (x: never) => never +>x : never + } +} + /* Template literals */ function testUnionWithTempalte(x: Basic) { @@ -1042,3 +1108,4 @@ function multipleGenericFuseWithBoth >xy : (X & number) | (Y & number) } } + diff --git a/tests/cases/compiler/narrowingByTypeofInSwitch.ts b/tests/cases/compiler/narrowingByTypeofInSwitch.ts index cd726827456e3..8f6e23e1b1f9b 100644 --- a/tests/cases/compiler/narrowingByTypeofInSwitch.ts +++ b/tests/cases/compiler/narrowingByTypeofInSwitch.ts @@ -1,5 +1,4 @@ -// @strictNullChecks: true -// @strictFunctionTypes: true +// @strict: true function assertNever(x: never) { return x; @@ -252,6 +251,20 @@ function narrowingNarrows(x: {} | undefined) { } } +function narrowingNarrows2(x: true | 3 | 'hello' | undefined) { + switch (typeof x) { + case 'number': assertNumber(x); return; + case 'boolean': assertBoolean(x); return; + case 'function': assertNever(x); return; + case 'symbol': assertNever(x); return; + case 'object': const _: {} = assertNever(x); return; + case 'string': assertString(x); return; + case 'undefined': assertUndefined(x); return; + case 'number': assertNever(x); return; + default: const _y: {} = assertNever(x); return; + } +} + /* Template literals */ function testUnionWithTempalte(x: Basic) { @@ -300,4 +313,4 @@ function multipleGenericFuseWithBoth case 'object': return [xy, 'two']; case `number`: return [xy] } -} \ No newline at end of file +}