변수에 값을 할당하고 타입을 부여하는 두 가지 방법
interface Person { name: string };
const alice: Person = { name: 'Alice' } // 1. 타입 선언
const bob = { name: 'Bob' } as Person; // 2. 타입 단언
const alice: Person = {}; // ❌ 'Person' 유형에 필요한 'name' 속성이 '{}' 유형에 없습니다.
const bob = {} as Person; // 오류 없음
👎 타입 단언은 강제로 타입을 지정했으니 타입 체커에게 오류를 무시하라고 한다. 또한 타입 선언문에서는 잉여 속성 체크가 동작하지만 단언문에서는 동작하지 않는다.
✔ 화살표 함수의 반환 타입 선언 방법
const people = ['alice', 'bob', 'jan'].map(
name => ({name})
); // type : {name: string; }[]
const people: Person[] = ['alice', 'bob', 'jan'].map(
(name): Person => ({name})
); // type : Person[]
타입 단언이 꼭 필요한 경우가 아니라면, 안정성 체크도 되는 타입 선언을 사용하자
❓ 타입 단언이 꼭 필요한 경우는 ?
👉 타입체커가 추론한 타입보다 내가 판단하는 타입이 더 정확할 때 사용
document.querySelector('#myBtn').addEventListener('click', e => {
e.currentTarget // type : EventTarget
const btn = e.currentTarget as HTMLButtonElement;
button // type : HTMLButtonElement
});
위와 같이 타입스크립트는 DOM에 접근할 수 없기 때문에 #myBtn이 버튼 요소인지 알지 못한다. 따라서 이벤트의 currentTarget이 같은 버튼요소인지도 알지 못하기 때문에 이런 경우는 타입 단언문을 쓰는 것이 타당하다.
const elNull = document.getElementById('foo'); // type : HTMLElement | null
const el = document.getElementById('foo')!; // type : HTMLElement
위에서 el 변수의 접미사로 쓰인 !는 그 값이 null이 아니라는 단언문으로 해석된다.
❗ 타입 단언문으로 임의의 타입 간에 변환을 할 수는 없다.
interface Person { name: string; }
const body = document.body;
const el = body as Person; // ❌ Error
'stringExample'.charAt(3);
기본형인 string의 경우 메서드를 가지고 있는 것처럼 보이지만 charAt은 string의 메서드가 아니다. string 기본형에 charAt 같은 메서드를 사용할 때, 자바스크립트는 기본형을 String 객체로 래핑하고, 메서드를 호출하고, 마지막에 래핑한 객체를 버린다.
✔ 타입스크립트는 기본형과 객체 래퍼 타입을 별도로 모델링한다.
string - String, number - Number, boolean - Boolean ...
❗ string은 String에 할당할 수 있지만 String은 string에 할당할 수 없다
타입스크립트 객체 래퍼 타입은 지양하고, 대신 기본형 타입을 사용하자.
String 대신 string, Number 대신 number, Boolean 대신 boolean
interface Options {
title: string;
darkMode?: boolean;
}
const o1: Options = { darkmode: true, title: 'Hello' };
// ❌ 'Options' 형식에 'darkmode'가 없습니다.
const intermediate = { darkmode: true, title: 'Hello' };
const o2: Options = intermediate; // 정상
const o3 = { darkmode: true, title: 'Hello' } as Options; // 정상
o1 변수에서 오른쪽은 객체 리터럴이지만, o2의 오른쪽은 객체 리터럴이 아니기 때문에 잉여 속성 체크가 적용되지 않고 에러가 사라진다. 잉여 속성 체크는 타입 단언문을 사용할 때에도 적용되지 않기 때문에 o3도 에러를 출력하지 않는다.
잉여 속성 체크는 오류를 찾는 효과적인 방법이지만, 타입스크립트 타입 체커가 수행하는 일반적인 구조적 할당 가능성 체크와 역할이 다르다.
타입스크립트에서는 함수 표현식을 사용하는 것이 좋다. 함수의 매개변수부터 반환값까지 전체를 함수 타입으로 선언하여 함수 표현식에 재사용할 수 있다는 장점이 있기 때문이다.
type DiceRollFn = (sides: number) => number;
const rollDice: DiceRollFn = sides => { /* ... */ };
✔ 다른 함수의 시그니처를 참조하려면 typeof fn을 사용하면 된다.
✔ 만약 같은 타입 시그니처를 반복적으로 작성한 코드가 있다면 함수 타입을 분리해 내거나 이미 존재하는 타입을 찾아보는 것이 좋다.