리터럴 타입은 특정 값 자체 를 타입으로 사용하는 것이고, 타입 좁히기는 넓은 타입을 좁은 타입으로 분기하는 것입니다.

리터럴 타입(Literal Type)

TYPESCRIPT
// string 타입이 아니라 'hello'라는 값 자체가 타입
let greeting: 'hello' = 'hello';
// greeting = 'world'; // ❌ Error: '"world"'는 '"hello"'에 할당 불가

// 숫자 리터럴
let zero: 0 = 0;

// 불리언 리터럴
let isTrue: true = true;

리터럴 타입 단독으로는 별로 쓸모 없어 보이지만, ** 유니온과 결합 **하면 강력해집니다.

TYPESCRIPT
// 특정 값만 허용하는 타입
type Direction = 'up' | 'down' | 'left' | 'right';

function move(direction: Direction) {
  console.log(`${direction} 방향으로 이동`);
}

move('up');      // OK
// move('diagonal'); // ❌ Error
TYPESCRIPT
// HTTP 메서드를 리터럴 유니온으로 제한
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

function request(method: HttpMethod, url: string) {
  // ...
}

request('GET', '/api/users');    // OK
// request('PATCH', '/api/users'); // ❌ Error

면접에서 "enum 대신 리터럴 유니온을 쓰는 이유"를 물어보기도 합니다. 리터럴 유니온은 트리쉐이킹이 되고, enum보다 가볍습니다.

타입 좁히기(Type Narrowing)

유니온 타입의 값을 사용할 때, 특정 분기 안에서 타입을 좁히는 것을 말합니다.

typeof 가드

TYPESCRIPT
function padLeft(value: string | number, padding: string | number): string {
  // typeof로 타입 좁히기
  if (typeof padding === 'number') {
    // 이 블록 안에서 padding은 number
    return ' '.repeat(padding) + value;
  }
  // 이 블록에서 padding은 string
  return padding + value;
}

typeof로 체크할 수 있는 타입은 제한적입니다.

TYPESCRIPT
// typeof가 반환하는 문자열
// 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function'

// ⚠️ null도 'object'를 반환함에 주의
typeof null; // 'object' — JavaScript의 유명한 버그

in 연산자 가드

객체에 특정 속성이 있는지 확인해서 타입을 좁힙니다.

TYPESCRIPT
type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    // animal: Fish
    animal.swim();
  } else {
    // animal: Bird
    animal.fly();
  }
}

instanceof 가드

클래스 인스턴스의 타입을 좁힐 때 사용합니다.

TYPESCRIPT
class HttpError {
  constructor(public status: number, public message: string) {}
}

class NetworkError {
  constructor(public message: string) {}
}

function handleError(error: HttpError | NetworkError) {
  if (error instanceof HttpError) {
    // error: HttpError
    console.log(`HTTP ${error.status}: ${error.message}`);
  } else {
    // error: NetworkError
    console.log(`네트워크 오류: ${error.message}`);
  }
}

동등성 비교로 좁히기

TYPESCRIPT
type Shape = 'circle' | 'square' | 'triangle';

function getArea(shape: Shape, size: number): number {
  if (shape === 'circle') {
    // shape: 'circle'
    return Math.PI * size * size;
  } else if (shape === 'square') {
    // shape: 'square'
    return size * size;
  } else {
    // shape: 'triangle'
    return (size * size * Math.sqrt(3)) / 4;
  }
}

truthiness로 좁히기

TYPESCRIPT
function printName(name: string | null | undefined) {
  if (name) {
    // name: string (null과 undefined가 제거됨)
    console.log(name.toUpperCase());
  } else {
    console.log('이름 없음');
  }
}

주의할 점은 빈 문자열 ''도 falsy이므로, 빈 문자열이 유효한 값인 경우에는 != null을 사용해야 합니다.

TYPESCRIPT
function printName(name: string | null) {
  if (name != null) {
    // 빈 문자열도 통과
    console.log(name);
  }
}

제어 흐름 분석(Control Flow Analysis)

TypeScript는 코드의 흐름을 분석해서 각 위치에서의 타입을 자동으로 추론합니다.

TYPESCRIPT
function example(x: string | number | boolean) {
  // x: string | number | boolean

  if (typeof x === 'string') {
    // x: string
    return x.toUpperCase();
  }

  // x: number | boolean (string이 제거됨)

  if (typeof x === 'number') {
    // x: number
    return x.toFixed(2);
  }

  // x: boolean (string, number가 모두 제거됨)
  return x ? 'yes' : 'no';
}

as const와 리터럴 타입 보존

TYPESCRIPT
// 일반 객체는 속성이 넓은 타입으로 추론됨
const config = {
  method: 'GET',  // string
  url: '/api',    // string
};

// as const로 리터럴 타입을 보존
const configConst = {
  method: 'GET',  // 'GET'
  url: '/api',    // '/api'
} as const;

// 함수에서 활용
function request(method: 'GET' | 'POST', url: string) {}

// request(config.method, config.url);      // ❌ Error: string은 'GET' | 'POST'에 할당 불가
request(configConst.method, configConst.url); // ✅ OK

정리

  • 리터럴 타입은 특정 값 자체를 타입으로 사용하는 것이다
  • 리터럴 유니온은 enum의 가벼운 대안이다
  • 타입 좁히기 도구: typeof, in, instanceof, 동등성 비교, truthiness
  • TypeScript의 제어 흐름 분석이 분기마다 자동으로 타입을 좁혀 준다
  • as const로 객체의 속성을 리터럴 타입으로 고정할 수 있다
댓글 로딩 중...