[Typescript] 타입 넓히기

김유진·2023년 4월 8일
0

Effective-TypeScript

목록 보기
12/28

타입스크립트에는 넓히기 라는 개념이 존재한다.
타입스크립트에서 작성된 코드를 체크하는 정적 분석 시점에, 변수는 가능한 값들의 집합인 타입을 가진다.

변수가 초기화 될 때, 타입을 명시하지 않으면 타입 체커는 직접 타입을 결정해야 한다. 그래서 값을 가지고 할당 가능한 값들의 집합을 유추해야 한다. 이것을 바로 넓히기라고 하는 것이다.

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

Vector3 함수를 사용한 코드는 런타임에 오류가 없지만, 편집기에서는 오류가 발생한다.

let x = 'x';
let vec = {x: 10, y: 20, z: 30};
getComponent(vec, x);

바로 string 형식의 인수는 x | y | z에 할당될 수 없다는 것이다.
getComponent는 두번째 매개변수에 x | y | z 타입을 기대하였으나, 할당 시점에 넓히기가 동작하여 string으로 추론되었기 때문에 오류가 발생한 것이다.

어떤 과정으로 넓히기가 일어나는지 자세히 알아보도록 하자.

const mixed = ['x', 1];

mixed가 가질 수 있는 타입은 현재 정보로서 추론하기 어렵기 때문에 어떤 타입으로 추론되어야 하는지 알 수 없다. 그래서 타입스크립트는 작성자의 의도를 추측하지만, 추측한 답이 항상 옳다고 할 수 없는 노릇이다..

타입스크립트의 넓히기 과정 제어

타입스크립트에서 넓히기 과정을 제어하여 예상하지 못한 오류가 발생하는 것을 방지할 수 있다.

1. const 변수로 선언하기

const x = 'x';  //타입이 x가 된다.
let vec = { x: 10, y:20, z: 30 };
getComponent(vec, x) //정상

x는 재할당될 수 없다는 것을 타입스크립트는 알고 있다. 그렇기 때문에 의심의 여지 없이 가장 좁은 타입인 x로 추론하는 것이다.
문자 리터럴 타입은 x | y | z에 할당될 수 있으므로 코드가 타입 체커를 통과하게 된다.

하지만 const가 만능은 아니다..

const를 사용하면 문제가 해결되는 것처럼 보이기는 하나, 객체와 배열은 여전히 해결되지 않는다.

const v = {
	x: 1,
};
v.x = 3;
v.x = '3';
v.y = 4;
v.name = 'Pythagoras';

v의 타입은 매우 다양하게 추론될 수 있다.
{readonly x: 1}, { x: number }, {[key : string]: number } 또는 더욱 추상화시킨다면 object 타입이 된 것이다. 객체는 타입스크립트의 넓히기 개념으로 let을 할당된 것처럼 다뤄서, v의 타입은 { x: number }가 된다. 그러므로 x에는 string으로 할당할 수 도 없고, 새로운 속성을 추가할 수도 없다.
이러한 타입 추론은 명확성과 유연성 사이의 균형을 유지하려고 한다. 오류를 수정하기 위해서는 구체적으로 타입을 추론해야 하지만, 잘못된 추론을 할 정도로 구체적으로 수행하지는 않는다.

타입 추론의 강도 직접 제어하기

1. 명시적 타입 구문 제공

const v: { x: 1|3|5 } = {
  x: 1,
}; //타입이 { x: 1|3|5 }

2. 타입 체커에 추가적인 문맥 제공

함수의 매개변수로 값을 전달한다던가..

3. const 단언문 사용

변수 선언에 사용되는 const와는 혼동하면 안된다.

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

값 뒤에 as const를 작성하면, 타입스크립트는 최대한 좁은 타입으로 추론하여 넓히기가 작용되지 않는다.

const a1 = [1, 2, 3]; // 타입은 number[]
const a2 = [1, 2, 3] as const; //타입은 readonly [1, 2, 3]

0개의 댓글