본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로프로그래밍 › TypeScript › 언어 기능

1. 타입시스템, 유니언과 리터럴

목차

TypeScript 타입 시스템: 유니언과 리터럴

TypeScript는 JavaScript 값에 대한 정적 검사를 더한다. string, number, boolean, bigint, symbol, null, undefined 같은 원시 값의 타입을 표현하고, 객체와 함수의 모양도 검사한다. 검사 결과는 개발 중 오류를 일찍 찾는 데 쓰이지만 컴파일 뒤 타입 선언은 사라진다. 서버 응답이나 사용자 입력이 실제로 선언한 타입인지 자동으로 검증해 주지는 않는다.

추론과 명시적 타입

let singer = "Ella";  // string으로 추론
singer = "Nina";
// singer = 19;         // 컴파일 오류

let selected: string | null = null;
selected = "Ella";

초기값이 있으면 보통 타입을 추론하므로 모든 변수에 애너테이션을 붙일 필요는 없다. selected는 null과 문자열을 모두 허용해야 하므로 string | null을 명시한다. let value;처럼 초기값 없이 선언한 변수는 검사 설정과 이후 사용에 따라 타입이 넓어질 수 있어, 공개 API나 중요한 상태라면 의도한 타입을 명시하는 편이 낫다.

console.logs(...)처럼 없는 속성을 호출하면 타입 검사 오류가 난다. 문법 오류와 타입 오류는 다르다. 타입 오류가 있어도 설정에 따라 JavaScript 출력이 만들어질 수 있으므로 배포 단계에서는 타입 검사 명령의 종료 코드를 확인한다.

유니언 타입은 사용 전 좁힌다

function describe(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase();
  }
  return value.toFixed(1);
}
console.log(describe("hello")); // HELLO
console.log(describe(3));       // 3.0

string | number에는 두 타입의 공통으로 안전한 연산만 바로 쓸 수 있다. typeof 검사 뒤 참인 가지에서는 문자열, 다른 가지에서는 숫자로 좁혀진다. 이것이 내로잉(narrowing)이다. typeof null은 "object"이므로 객체 여부를 검사할 때는 value !== null도 함께 확인한다. 선언한 유니언 타입은 모든 입력 값이 올바르다고 보장하는 런타임 검증은 아니다.

리터럴 타입과 판별 유니언

type State = "idle" | "loading" | "done";
let state: State = "idle";
state = "loading";
// state = "finished"; // 컴파일 오류

type Result =
  | { kind: "success"; value: string }
  | { kind: "failure"; message: string };

function show(result: Result): string {
  if (result.kind === "success") return result.value;
  return `오류: ${result.message}`;
}

State는 아무 문자열이 아니라 정해진 세 값만 허용한다. Result는 kind에 따라 필요한 필드가 달라진다. kind === "success" 검사 뒤에는 value가 있다는 사실을 컴파일러가 안다. 한 객체 타입에서 value?와 message?를 모두 선택적으로 선언하면 성공인데 값이 빠진 모순된 상태를 허용할 수 있으므로 판별 유니언이 더 정확하다.

배열·객체의 묶임을 읽기

let textOrNumbers: string | number[] = "ready";
textOrNumbers = [1, 2];
let mixed: (string | number)[] = ["A", 2];

type Poet = { born: number; name: string };
const poet: Poet = { born: 1935, name: "Sara" };

괄호 위치에 따라 string | number[]는 문자열 하나 또는 숫자 배열, (string | number)[]는 여러 원소가 각각 문자열이나 숫자일 수 있는 배열이다. TypeScript의 객체 타입은 이름이 같아야 하는 명목 타입보다는 필요한 구조가 있는지를 비교한다. 그래서 { born, name, extra } 값을 먼저 변수에 담았다면 Poet가 필요한 곳에 전달할 수 있다. 그러나 외부 JSON에서 두 필드의 실제 타입을 확인하려면 typeof 등의 런타임 검증을 별도로 작성한다.

참고: TypeScript Everyday Types, Narrowing

같은 카테고리의 글