Menu

TypeScript switch case: 타입 좁히기와 전수 검사

TypeScript의 switch 문: 문법, 각 case에서 유니언 타입이 좁혀지는 방식, case가 빠지면 컴파일되지 않는 전수 검사(exhaustive) switch, switch (true) 패턴, fallthrough와 블록 스코프.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

TypeScript switch 문은 타입 검사가 더해진 JavaScript의 switch입니다. 값을 각 case와 ===로 비교해 일치하는 분기를 실행하고, break나 return에서 멈춥니다. TypeScript는 각 case 안에서 switch 대상 값을 좁히고, 가능한 모든 값을 처리했는지 검사할 수 있습니다.

겹쳐 쓴 레이블(case "sat": case "sun":)은 한 분기를 공유합니다. return은 함수를 끝내므로 그 뒤에 break가 필요 없습니다.

문법

switch (expression) {
    case value1:
        // runs when expression === value1
        break;
    case value2:
    case value3:
        // runs for value2 or value3
        break;
    default:
        // runs when nothing else matched
}
  • 일치 여부는 엄격한 동등 비교 ===로 판단합니다. case 1은 문자열 "1"과 일치하지 않습니다.
  • break, return, throw가 없으면 실행이 다음 case로 이어집니다(fallthrough).
  • default는 선택 사항이며 어디에 둬도 되지만, 관례상 마지막에 둡니다.
  • 절대 일치할 수 없다고 TypeScript가 증명할 수 있는 case 값은 TS2678 오류입니다. "a" | "b" 타입의 매개변수에 case "c":를 쓰면 Type '"c"' is not comparable to type '"a" | "b"'.를 보고하므로 case 레이블의 오타를 잡아 줍니다.

각 case 안에서의 타입 좁히기

각 case에서 TypeScript는 어떤 값이 일치했는지 알고 타입을 좁힙니다. 판별 유니언(discriminated union)과 함께 쓸 때 가장 유용합니다. 공통 태그 프로퍼티로 switch하면 각 case는 자신의 프로퍼티를 가진 해당 변형을 봅니다.

shape.radius는 "circle" case에서만 컴파일됩니다. 그 밖에서는 shape가 radius가 없는 사각형일 수 있습니다. default가 없어도 함수는 number를 반환하는 것으로 타입 검사를 통과합니다. 세 case가 모든 kind를 다룬다는 것을 TypeScript가 알기 때문입니다. 이를 바탕으로 한 더 많은 패턴은 판별 유니언 페이지에 있습니다.

never를 이용한 전수 검사 switch

위 함수는 case 없이 네 번째 도형을 추가하면 컴파일되지 않습니다(TS2366, 반환 누락). 하지만 이는 함수가 값을 반환할 때만 통합니다. void 코드에서도 동작하고 메시지도 더 분명한 보장이 필요하다면, 값을 never에 대입하는 default를 추가하세요.

index.ts(14,19): error TS2322: Type '{ kind: "triangle"; base: number; height: number; }' is not assignable to type 'never'.

처리한 case들 뒤에 shape에 남은 타입은 삼각형 변형뿐이고, 이는 never에 대입할 수 없습니다. 오류는 무엇이 빠졌는지 정확히 알려 줍니다. case "triangle": return (shape.base * shape.height) / 2;를 추가하면 default에서는 never가 되므로 블록이 컴파일되고 9를 출력합니다. throw는 타입이 예상하지 못한 JSON의 kind 같은 잘못된 데이터를 런타임에 계속 막아 줍니다.

많은 코드베이스는 이 검사를 헬퍼 함수로 감쌉니다.

enum에도 같은 기법이 통합니다. enum 값으로 switch하고 default에서 assertNever에 넘기세요.

범위와 조건을 위한 switch (true)

switch는 값을 비교하므로 범위를 다루려면 요령이 필요합니다. true로 switch하고 각 case에 조건을 씁니다. TypeScript 5.3부터 이 조건들은 if 문과 똑같이 타입을 좁힙니다.

case는 순서대로 검사되므로 가장 구체적인 것을 먼저 두세요. 이것이 if/else if보다 읽기 좋은지는 취향 문제이고, 동작은 같습니다.

fallthrough와 break

코드가 있는데 break, return, throw가 없는 case는 다음 case로 넘어갑니다(fallthrough). 이는 거의 항상 버그입니다. 컴파일러 옵션 noFallthroughCasesInSwitch(strict에 포함되지 않음)를 켜면 이를 TS7029 오류 Fallthrough case in switch.로 만들며, 비어 있는 레이블을 겹쳐 쓰는 것은 계속 허용합니다.

이처럼 의도한 fallthrough도 동작하지만, noFallthroughCasesInSwitch를 켜면 별도의 if 검사 등으로 다시 작성해야 합니다. 대개는 그편이 더 명확합니다.

case 안의 변수

switch 본문 전체가 하나의 블록이므로 한 case에서 선언한 const는 다른 case에서도 보이고, 다시 선언하면 오류입니다. case를 중괄호로 감싸면 자기만의 스코프가 생깁니다.

중괄호가 없으면 두 번째 const unit은 컴파일 오류 TS2451 Cannot redeclare block-scoped variable 'unit'.입니다.

switch와 객체 조회 비교

각 case가 값을 다른 값으로 매핑하기만 한다면 Record로 타입을 지정한 객체가 더 짧고, TypeScript가 모든 키가 있는지 검사합니다.

키를 빠뜨리면 컴파일 오류이므로 never 검사와 같은 전수성을 얻습니다. switch는 case마다 다른 로직을 실행하거나, 유니언 변형을 좁히거나, 일찍 반환하는 경우에 쓰세요.

자주 묻는 질문

TypeScript에서 switch 문은 어떻게 쓰나요?

JavaScript와 똑같습니다: switch (value) { case "a": ...; break; default: ... }. case는 ===로 비교합니다. TypeScript는 검사를 더합니다. switch 대상 타입과 절대 일치할 수 없는 case 값은 오류(TS2678)이고, 각 case 안에서는 switch 대상 변수가 그 case로 좁혀집니다.

TypeScript에서 switch를 전수 검사(exhaustive)하려면 어떻게 하나요?

값을 never 타입 변수에 대입하는 default를 추가합니다: default: { const unreachable: never = value; throw new Error(...) }. 유니언의 모든 멤버를 처리했다면 그곳에서 값은 never이므로 컴파일됩니다. 하나라도 빠지면 컴파일러가 빠진 멤버를 보고합니다(TS2322).

TypeScript switch에서 여러 case를 같은 코드로 처리하려면 어떻게 하나요?

사이에 코드 없이 레이블을 겹쳐 씁니다: case "sat": case "sun": return "weekend";. 빈 case는 다음 case로 넘어갑니다. noFallthroughCasesInSwitch 옵션은 코드가 있으면서 break나 return이 없는 case만 보고하므로 겹쳐 쓴 레이블은 계속 허용됩니다.

TypeScript에서 switch (true)도 타입을 좁히나요?

예, TypeScript 5.3부터 좁힙니다. switch (true) { case typeof x === "string": ... }에서 그 case 안의 x는 if에서와 마찬가지로 string으로 좁혀집니다. 범위나 타입을 검사하는 if/else if 연쇄를 대신하는 읽기 쉬운 방법입니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기