Skip to content

Commit f07ecf8

Browse files
committed
Support template literals in swith with typeof narrowing
1 parent 94e2743 commit f07ecf8

File tree

5 files changed

+483
-3
lines changed

5 files changed

+483
-3
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16652,8 +16652,8 @@ namespace ts {
1665216652
const witnesses: (string | undefined)[] = [];
1665316653
for (const clause of switchStatement.caseBlock.clauses) {
1665416654
if (clause.kind === SyntaxKind.CaseClause) {
16655-
if (clause.expression.kind === SyntaxKind.StringLiteral) {
16656-
witnesses.push((clause.expression as StringLiteral).text);
16655+
if (isStringLiteralLike(clause.expression)) {
16656+
witnesses.push(clause.expression.text);
1665716657
continue;
1665816658
}
1665916659
return emptyArray;

tests/baselines/reference/narrowingByTypeofInSwitch.js

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,56 @@ function narrowingNarrows(x: {} | undefined) {
249249
default: const _y: {} = x; return;
250250
}
251251
}
252-
252+
253+
/* Template literals */
254+
255+
function testUnionWithTempalte(x: Basic) {
256+
switch (typeof x) {
257+
case `number`: assertNumber(x); return;
258+
case `boolean`: assertBoolean(x); return;
259+
case `function`: assertFunction(x); return;
260+
case `symbol`: assertSymbol(x); return;
261+
case `object`: assertObject(x); return;
262+
case `string`: assertString(x); return;
263+
case `undefined`: assertUndefined(x); return;
264+
}
265+
assertNever(x);
266+
}
267+
268+
function fallThroughTestWithTempalte(x: string | number | boolean | object) {
269+
switch (typeof x) {
270+
case `number`:
271+
assertNumber(x)
272+
case `string`:
273+
assertStringOrNumber(x)
274+
break;
275+
default:
276+
assertObject(x);
277+
case `number`:
278+
case `boolean`:
279+
assertBooleanOrObject(x);
280+
break;
281+
}
282+
}
283+
284+
function keyofNarrowingWithTemplate<S extends { [K in keyof S]: string }>(k: keyof S) {
285+
function assertKeyofS(k1: keyof S) { }
286+
switch (typeof k) {
287+
case `number`: assertNumber(k); assertKeyofS(k); return;
288+
case `symbol`: assertSymbol(k); assertKeyofS(k); return;
289+
case `string`: assertString(k); assertKeyofS(k); return;
290+
}
291+
}
292+
293+
/* Both string literals and template literals */
294+
295+
function multipleGenericFuseWithBoth<X extends L | number, Y extends R | number>(xy: X | Y): [X, number] | [Y, string] | [(X | Y)] {
296+
switch (typeof xy) {
297+
case `function`: return [xy, 1];
298+
case 'object': return [xy, 'two'];
299+
case `number`: return [xy]
300+
}
301+
}
253302

254303
//// [narrowingByTypeofInSwitch.js]
255304
function assertNever(x) {
@@ -585,3 +634,70 @@ function narrowingNarrows(x) {
585634
return;
586635
}
587636
}
637+
/* Template literals */
638+
function testUnionWithTempalte(x) {
639+
switch (typeof x) {
640+
case "number":
641+
assertNumber(x);
642+
return;
643+
case "boolean":
644+
assertBoolean(x);
645+
return;
646+
case "function":
647+
assertFunction(x);
648+
return;
649+
case "symbol":
650+
assertSymbol(x);
651+
return;
652+
case "object":
653+
assertObject(x);
654+
return;
655+
case "string":
656+
assertString(x);
657+
return;
658+
case "undefined":
659+
assertUndefined(x);
660+
return;
661+
}
662+
assertNever(x);
663+
}
664+
function fallThroughTestWithTempalte(x) {
665+
switch (typeof x) {
666+
case "number":
667+
assertNumber(x);
668+
case "string":
669+
assertStringOrNumber(x);
670+
break;
671+
default:
672+
assertObject(x);
673+
case "number":
674+
case "boolean":
675+
assertBooleanOrObject(x);
676+
break;
677+
}
678+
}
679+
function keyofNarrowingWithTemplate(k) {
680+
function assertKeyofS(k1) { }
681+
switch (typeof k) {
682+
case "number":
683+
assertNumber(k);
684+
assertKeyofS(k);
685+
return;
686+
case "symbol":
687+
assertSymbol(k);
688+
assertKeyofS(k);
689+
return;
690+
case "string":
691+
assertString(k);
692+
assertKeyofS(k);
693+
return;
694+
}
695+
}
696+
/* Both string literals and template literals */
697+
function multipleGenericFuseWithBoth(xy) {
698+
switch (typeof xy) {
699+
case "function": return [xy, 1];
700+
case 'object': return [xy, 'two'];
701+
case "number": return [xy];
702+
}
703+
}

tests/baselines/reference/narrowingByTypeofInSwitch.symbols

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,3 +715,144 @@ function narrowingNarrows(x: {} | undefined) {
715715
}
716716
}
717717

718+
/* Template literals */
719+
720+
function testUnionWithTempalte(x: Basic) {
721+
>testUnionWithTempalte : Symbol(testUnionWithTempalte, Decl(narrowingByTypeofInSwitch.ts, 249, 1))
722+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
723+
>Basic : Symbol(Basic, Decl(narrowingByTypeofInSwitch.ts, 46, 1))
724+
725+
switch (typeof x) {
726+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
727+
728+
case `number`: assertNumber(x); return;
729+
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
730+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
731+
732+
case `boolean`: assertBoolean(x); return;
733+
>assertBoolean : Symbol(assertBoolean, Decl(narrowingByTypeofInSwitch.ts, 6, 1))
734+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
735+
736+
case `function`: assertFunction(x); return;
737+
>assertFunction : Symbol(assertFunction, Decl(narrowingByTypeofInSwitch.ts, 18, 1))
738+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
739+
740+
case `symbol`: assertSymbol(x); return;
741+
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
742+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
743+
744+
case `object`: assertObject(x); return;
745+
>assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1))
746+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
747+
748+
case `string`: assertString(x); return;
749+
>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1))
750+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
751+
752+
case `undefined`: assertUndefined(x); return;
753+
>assertUndefined : Symbol(assertUndefined, Decl(narrowingByTypeofInSwitch.ts, 30, 1))
754+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
755+
}
756+
assertNever(x);
757+
>assertNever : Symbol(assertNever, Decl(narrowingByTypeofInSwitch.ts, 0, 0))
758+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 253, 31))
759+
}
760+
761+
function fallThroughTestWithTempalte(x: string | number | boolean | object) {
762+
>fallThroughTestWithTempalte : Symbol(fallThroughTestWithTempalte, Decl(narrowingByTypeofInSwitch.ts, 264, 1))
763+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37))
764+
765+
switch (typeof x) {
766+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37))
767+
768+
case `number`:
769+
assertNumber(x)
770+
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
771+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37))
772+
773+
case `string`:
774+
assertStringOrNumber(x)
775+
>assertStringOrNumber : Symbol(assertStringOrNumber, Decl(narrowingByTypeofInSwitch.ts, 38, 1))
776+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37))
777+
778+
break;
779+
default:
780+
assertObject(x);
781+
>assertObject : Symbol(assertObject, Decl(narrowingByTypeofInSwitch.ts, 22, 1))
782+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37))
783+
784+
case `number`:
785+
case `boolean`:
786+
assertBooleanOrObject(x);
787+
>assertBooleanOrObject : Symbol(assertBooleanOrObject, Decl(narrowingByTypeofInSwitch.ts, 42, 1))
788+
>x : Symbol(x, Decl(narrowingByTypeofInSwitch.ts, 266, 37))
789+
790+
break;
791+
}
792+
}
793+
794+
function keyofNarrowingWithTemplate<S extends { [K in keyof S]: string }>(k: keyof S) {
795+
>keyofNarrowingWithTemplate : Symbol(keyofNarrowingWithTemplate, Decl(narrowingByTypeofInSwitch.ts, 280, 1))
796+
>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36))
797+
>K : Symbol(K, Decl(narrowingByTypeofInSwitch.ts, 282, 49))
798+
>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36))
799+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
800+
>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36))
801+
802+
function assertKeyofS(k1: keyof S) { }
803+
>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87))
804+
>k1 : Symbol(k1, Decl(narrowingByTypeofInSwitch.ts, 283, 26))
805+
>S : Symbol(S, Decl(narrowingByTypeofInSwitch.ts, 282, 36))
806+
807+
switch (typeof k) {
808+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
809+
810+
case `number`: assertNumber(k); assertKeyofS(k); return;
811+
>assertNumber : Symbol(assertNumber, Decl(narrowingByTypeofInSwitch.ts, 2, 1))
812+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
813+
>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87))
814+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
815+
816+
case `symbol`: assertSymbol(k); assertKeyofS(k); return;
817+
>assertSymbol : Symbol(assertSymbol, Decl(narrowingByTypeofInSwitch.ts, 14, 1))
818+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
819+
>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87))
820+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
821+
822+
case `string`: assertString(k); assertKeyofS(k); return;
823+
>assertString : Symbol(assertString, Decl(narrowingByTypeofInSwitch.ts, 10, 1))
824+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
825+
>assertKeyofS : Symbol(assertKeyofS, Decl(narrowingByTypeofInSwitch.ts, 282, 87))
826+
>k : Symbol(k, Decl(narrowingByTypeofInSwitch.ts, 282, 74))
827+
}
828+
}
829+
830+
/* Both string literals and template literals */
831+
832+
function multipleGenericFuseWithBoth<X extends L | number, Y extends R | number>(xy: X | Y): [X, number] | [Y, string] | [(X | Y)] {
833+
>multipleGenericFuseWithBoth : Symbol(multipleGenericFuseWithBoth, Decl(narrowingByTypeofInSwitch.ts, 289, 1))
834+
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37))
835+
>L : Symbol(L, Decl(narrowingByTypeofInSwitch.ts, 132, 1))
836+
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58))
837+
>R : Symbol(R, Decl(narrowingByTypeofInSwitch.ts, 134, 31))
838+
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81))
839+
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37))
840+
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58))
841+
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37))
842+
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58))
843+
>X : Symbol(X, Decl(narrowingByTypeofInSwitch.ts, 293, 37))
844+
>Y : Symbol(Y, Decl(narrowingByTypeofInSwitch.ts, 293, 58))
845+
846+
switch (typeof xy) {
847+
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81))
848+
849+
case `function`: return [xy, 1];
850+
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81))
851+
852+
case 'object': return [xy, 'two'];
853+
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81))
854+
855+
case `number`: return [xy]
856+
>xy : Symbol(xy, Decl(narrowingByTypeofInSwitch.ts, 293, 81))
857+
}
858+
}

0 commit comments

Comments
 (0)