✒️ 추론 가능한 타입 사용으로 효율적인 추론하기
✏️ 타입 체커의 추론을 믿자
let x : number = 12;
let y = 12;
const person: {name: string} = {name: 'baik gwangin'};
const person = {name: 'baik gwangin'};
const axis1: string = 'x';
const axis2 = 'y';
- 타입스크립트는 타입을 위한 언어이지만, 모든 변수에 타입을 선언 하는 것은 대단히 비효율적인 스타일이다.
- 타입 체커는 변수의 초기값을 통해 타입을 추론할 수 있으며, 이는 원시 값이 아닌 객체, 배열도 포함된다.
interface Product {
id: number;
name: string;
price: number;
}
function logProduct(product: Product) {
const {id, name, price} = product;
console.log(id, name, price);
}
app.get("/", (req, res) => {
res.json({isSuccess: true, result: 'OK'})
})
- 비구조화 할당 의 경우 할당된 변수의 타입을 자동으로 추론해준다.
- 정보가 부족해서 타입 체커가 타입을 판단하기 어려운 경우 (함수의 매개변수) 도 있지만, 그렇지 않은 경우가 더 많다.
- TS 에서 변수의 타입은 선언부 에서 결정된다. 이를 활용하여 타입 구문을 최대한 생략하면 코드가 클린해진다.
✏️ 가끔 타입을 명시하고 싶다면 언제?
interface Product {
id: number;
name: string;
price: number;
}
const product = {
id: 1212122,
name: 'Kurby',
price: 20000
}
logProduct(product);
- 객체 리터럴을 정의할 경우, 잉여 속성 체크가 동작하여 선택적 속성이 있는 타입의 오류를 쉽게 잡아낼 수 있다.
- 만약 타입 구문을 제거한다면 잉여 속성 체크가 동작하지 않아 객체의 선언부 가 아닌 사용 단계 에서 오류가 발생한다.
- 함수의 반환 타입에도 타입을 명시하면 좋다. 보다 직관적으로 반환 값을 추론할 수 있으며 입력과 출력 타입이 무엇인지도 알아야 하기에 함수를 제대로 설계할 수 있다.
✒️ 다른 타입에는 다른 변수 사용하기
✏️ 변수의 값은 변하나 타입은 변하지 않는다.
let id = '12-34-56';
id = 123456;
let id: string | number = '12-34-56';
id = 123456;
let id = '12-34-56';
let serial = 123456;
- JS 에서는 하나의 변수에 다른 타입의 값을 할당할 수 있으나, TS에서는 불가능하다.
- 타입을 바꿀 수 있는 방법은 Type Narrowing 을 통해 타입을 좁히는 것이며, 새로운 타입을 포함하도록 확장하는 것이 아니다.
- 이런 경우 유니온 타입을 지정하기 보다는 별도의 변수 를 만들어 혼동을 막는 것이 더 좋다.
✒️ 타입 넓히기
✏️ 타입 넓히기란?
interface Vertor3 {
x: number;
y: number;
z: number;
}
function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') {
return vector[axis];
}
let x = 'x'
let y = {x: 10, y: 20, z: 10};
getComponent(y, x)
- 런타임에 모든 변수는 유일한 값을 가지지만, 타입 체커가 작동하는 정적 분석 시점에서 변수는 가능한 값들의 집합인 타입 을 가진다.
- 상수를 사용해서 변수를 초기화하면 타입 체커는 타입을 결정해야 하고, 할당된 단일 값을 기반으로 할당 가능한 값들의 집합을 유추해야 한다.
const mixed = ['x', 1];
- 이러한 과정을 넓히기 (widening) 라고 한다. 그러나 넓히기를 통해 추론된 타입은 항상 사용자의 의도와는 맞지 않는다.
✏️ const로 타입 넓히기를 제어해보자
const x = 'x'
let y = {x: 10, y: 20, z: 10};
getComponent(y, x)
const v = {
x: 1,
}
v.x = 'Gwangin'
const
키워드를 사용하여 넓히기를 제어할 수 있다. let
보다는 확실히 좁은 타입으로 추론하기 때문이다.
- 하지만 객체의 경우에는 문제가 해결되지 않는다. 이를 해결하기 위해서는 TS의 기본 동작 을 재정의 해야 한다.
✏️ TS의 기본 동작을 재정의하는 방법
const v: {x: 1 | 3 | 5} = {
x: 1,
}
function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z'): number {
return vector[axis];
}
const a1 = [1, 2, 3];
const a2 = [1, 2, 3] as const;
- 첫번째로는 명시적 타입 구문 을 제공한다. 초기 선언부에 타입을 명시하여 타입 추론을 의도대로 진행하도록 한다.
- 두번째로는 타입 체커에 추가적인 문맥을 제공하는 것이다. 예를 들면 함수의 리턴 타입이나 매개변수의 타입을 지정하는 것이다.
- 마지막으로
const
단언문을 사용하지 않아야 한다. 이 경우 타입 체커는 최대한 좁은 타입으로 추론하기 때문에 넓히기가 동작하지 않는다.
✒️ 타입 좁히기
✏️ 타입 좁히기란?
function checkItsType(x: unknown) {
if (typeof x === 'string') {
console.log('x is string');
}
}
const el = document.getElementById('foo');
if (typeof el === 'object') {
console.log(el)
}
- 타입 체커가 넓은 타입으로부터 좁은 타입으로 타입을 추론해나가는 과정을 타입 좁히기 (Type Narrowing) 라고 한다.
- 단, 타입을 좁히는 과정에서 실수가 나올 수 있으니 본인이 올바른 방법으로 타입을 좁혔는지 체크해야 한다.
✏️ 명시적 태그를 활용해 타입 좁히기
interface UploadEvent {type: 'upload'; filename: string; }
interface DownloadEvent {type: 'download'; filename: string; }
type Event = UploadEvent | DownloadEvent;
function handleEvent(e: AppEvent) {
if (e.type === 'upload') {
console.log(e)
} else {
console.log(e)
}
}
function isHTMLInputElement(el: HTMLElement): el is HTMLInputElement {
return 'value' in el;
}
const el = document.getElementById('foo');
if (isHTMLInputElement(el)) {
console.log(el)
}
- 태그된 유니온 을 활용하여 명시적으로 타입을 지정하고 이후 타입 좁히기를 진행할 수 있다.
- 또한 사용자 정의 타입 가드 를 통해서 타입 식별을 위한 커스텀 함수를 도입할 수도 있다.
✒️ 한꺼번에 객체 생성하기
✏️ 객체를 생성할 때는 한번에 생성하자.
const pt = {};
pt.x = 1;
const pt = {
x: 1;
}
pt.x = 2;
- 객체를 생성할 때는 속성을 하나씩 추가하지 말고 일괄적으로 추가하여 타입 추론을 유리하게 만들자.
interface Point {
x: number;
}
const pt = {} as Point;
pt.x = 1;
- 객체를 꼭 나눠서 만들어야 한다면
as
키워드를 통한 타입 단언으로 타입 체커를 통과할 수는 있다.
const nameTitle = {name: 'Baik'}
const birthDate = {year: 1999, month: 1, day: 26};
const Person = {...nameTitle, ...birthDate};
- 여러 객체를 모아 하나의 큰 객체를 생성할 때도 Spread 연산자 를 사용하여 한번에 객체를 생성하자.
- 만약 선택적 필드 (옵셔널) 방식으로 생성하려면 헬퍼 함수를 사용하여 부분적으로 적용하자.