Item 21 타입 넓히기

Widening (넓히기)

책의 Item 7 타입이 값들의 집합이라고 생각하기에서 설명한 것처럼(적어두진 않았다.) 런타임에 모든 변수는 유일한 값을 가진다.

그러나 타입스크립트가 작성된 코드를 체크하는 정적 분석 시점에, 변수는 '가능한' 값들의 집합인 타입을 가진다.

  • 상수를 사용해서 변수를 초기화할 때 타입을 명시하지 않으면 타입 체커가 타입을 결정해야 한다.

  • 이 말은 지정된 단일 타입 값을 가지고 할당 가능한 값들의 집합을 유추해야 한다는 뜻이다.

TS에서는 이러한 과정을 'widening'이라고 부른다.

interface Vector3 { x: number; y: number; z: number }
function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') {
  return vector[axis]
}

let x = 'x' // 'widening'이 동작해 'string'으로 추론
let vec = { x: 10, y: 20, z: 30 }
getComponent(vec, x)
               // ~ 'string' 형식의 인수는 '"x" | "y" | "z"' 형식의 매개변수에 할당될 수 없습니다.

실행은 (런타임에 오류 없이) 잘 되지만 편집기에서 오류가 발생한다.

const mixed = ['x', 1]

mixed의 타입이 될 수 있는 후보는 엄청 많다. 나열을 좀 해보자면...

  • ('x' | 1)[]

  • ['x', 1]

  • [string, number]

  • readonly [string, number]

  • (string|number)[]

  • readonly (string|number)[]

  • [any, any]

  • any[]

타입스크립트는 작성자의 의도를 추측하는데, 이 경우에는 (string|number)[]로 추측한다. 그렇지만 사람의 마음을 읽을 수는 없기 때문에 추측한 답이 항상 옳지도 않다.

Widening을 제어하는 방법 1: const

let 대신 const를 사용하여 변수를 선언하면 더 좁은 타입이 된다.

const x = 'x' // 타입이 'x'
let vec = { x: 10, y: 20, z: 30 }
getComponent(vec, x) // OK

이제 x는 재할당될 수 없으므로 TS는 더 좁은 타입인 타입 'x'로 추론한다.

하지만 객체와 배열의 경우에는 여전히 const로 추론하기 어렵다. 따라서 객체를 한번에 만들어야 하는데, 이 내용은 Item 23에서 다룬다.

Widening을 제어하는 방법 2: 타입 체커에 추가적인 문맥 제공하기

함수의 매개변수로 값을 전달하는 등 문맥을 제공하면 된다. Item 26에서 타입 추론 과정에서 문맥의 역할에 대한 내용을 다룬다.

Widening을 제어하는 방법 3: const assertion (const 단언문 사용하기)

const 단언문(const assertion)과 변수 선언에 쓰이는 let, const와 혼동하지 말자.

const v1 = {
  x: 1,
  y: 2,
}  // 타입은 { x: number; y: number }

const v2 = {
  x: 1 as const,
  y: 2,
}  // 타입은 { x: 1; y: number }

const v3 = {
  x: 1,
  y: 2,
} as const  // 타입은 { readonly x: 1; readonly y: 2 }

값 뒤에 as const를 작성하면, TS는 최대한 좁은 타입으로 추론한다.

Summary

  • 타입스크립트가 넓히기(widening)을 통해 상수의 타입을 추론하는 법을 이해하자.

  • 동작에 영향을 줄 수 있는 방법인 const, 타입 구문, 문맥, as const에 익숙해지자.

Last updated