아이템9 ~ 아이템 11까지 정리
- 아이템 9 : 타입 단언보다 타입 선언 사용하기
- 아이템10 : 객체 래퍼 타입 피하기
- 아이템11 : 잉여 속성 체크의 한계 인지하기
타입 단언보다 타입 선언 사용하기
- 자바스크립트에서 타입을 선언하는 방법은 두 가지가 있다.
- 타입 단언
- 타입 선언
interface Person { name: string };
// 타입 단언 방식
const alice: Person = { name: 'Alice' }; // Type is Person
// 타입 선언 방식
const bob = { name: 'Bob' } as Person; // Type is Person
- 타입 단언보다 타입 선언을 사용하는 것이 좋다.
- 타입 단언은 안정성 체크를 하지 않고, 강제로 타입을 지정하기 대문에 타입 체커에서 오류를 무시한다.
- 유형에 필요한 속성을 지정하지 않았을 때, 속성을 추가할 때도 마찬가지이다.
// name 속성을 지정하지 않았을 때
interface Person { name: string };
const alice: Person = {};
// ~~~~~ 'Person' 타입에 'name' 속성이 필요한데 '{}' 유형에 없다.
const bob = {} as Person; // 에러 없음
// 속성을 추가할 때
const alice: Person = {
name: 'Alice',
occupation: 'TypeScript developer'
// ~~~~~~~~~~ 'Person' 형식에 'occupation'가 없다.
// 개체 리터럴은 알려진 속성만 지정할 수 있다.
};
const bob = {
name: 'Bob',
occupation: 'JavaScript developer'
} as Person; // 에러 없음
// 해당 단언문은 타입 단언의 원래 문법이다.
// {} as Person과 똑같이 작동한다.
const bob = <Person>{};
// 쓰지 맙시다.
화살표 함수 안에서 타입 선언문
- 화살표 함수를 사용할 때 타입 단언문을 이용하면 정말 편하겠지만, 위와 마찬가지로 오류가 발생할 수 있다.
- 타입 선언문 예시
interface Person { name: string };
// 1. 변수를 사용하는 방법
const people = ['alice', 'bob', 'jan'].map(name => {
const person: Person = {name};
return person
}); // Type is Person[]
// 2. 화살표 함수를 사용하는 방법
// 함수 호출 체이닝이 연속되는 경우 체이닝 시작부터 명명된 타입을 가져야 한다.
// return 받을 타입이 Person[]임을 반드시 명시한다.
const people: Person[] = ['alice', 'bob', 'jan'].map(
// (name): Person은 name의 타입이 없고, 반환 타입이 Person이라는 것을 명시한다.
(name): Person => ({name})
); // Type is Person[]
타입 단언이 꼭 필요한 경우
- 타입 체커가 추론한 타입보다 개발자가 판단하는 타입이 더 정확할 때가 있다.
- ex ) DOM 엘리먼트에 대해 타입 추론을 할 때
- 타입스크립트는 DOM에 접근할 수 없다.
// tsConfig: {"strictNullChecks":false}
document.querySelector('#myButton').addEventListener('click', e => {
e.currentTarget // 타입은 EventTarget
const button = e.currentTarget as HTMLButtonElement;
button // 타입은 HTMLButtonElement
});
Null 단언문
- 단언문은 타입 체커가 알지 못하기 때문에 들어오는 값이 null이 아닌 것이 확실할 때 사용한다.
- null일수도 있다면 조건문을 사용해야 한다.
const elNull = document.getElementById('foo'); // Type is HTMLElement | null
const el = document.getElementById('foo')!; // Type is HTMLElement
객체 래퍼 타입 피하기
> ‘primitive’.charAt(3)
"m'
- 자바스크립트에서 코드를 실행했을 때 charAt이 string 타입의 메서드인 것처럼 보이지만, String 객체 타입의 메서드가 호출된 것이다.
- 키워드 : 래퍼 객체
- 타입스크립트는 기본형과 객체 래퍼 타입을 별도로 모델링한다.
- string과 String
- number와 Number
- boolean과 Boolean
- symbol과 Symbol
- bigInt와 BigInt
잉여 속성 체크의 한계 인지하기
예제 1)
interface Room {
numDoors: number;
ceilingHeightFt: number;
}
const r: Room = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present',
// ~~~~~~~~~~~~~~~~~~ Object literal may only specify known properties,
// and 'elephant' does not exist in type 'Room'
};
- 해당 코드를 구조적 타이핑 관점으로 생각해보면 오류가 발생하지 않아야 한다.
예제 2)
interface Room {
numDoors: number;
ceilingHeightFt: number;
}
// 임시 변수
const obj = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present',
};
const r: Room = obj; // OK
- 임시 변수 obj 객체는 Room 타입에 할당이 가능하다.
- 타입 체커가 추론한 obj 타입은 Room 타입의 부분 집합을 포함하므로 통과된다.
- 두 예제의 차이점은 예제 1에서는
잉여 속성 체크 과정
이 수행되었고 예제 2에서는 과정이 수행되지 않았다는 것이다.
타입스크립트는 런타임에 예외를 던지는 코드에 오류를 표시하는 것뿐 아니라, 의도와 다르게 작성한 코드까지 찾으려 한다는 것을 잊지 말자.
잉여 속성 체크란?
- 구조적 타입 시스템에서 발생할 수 있는 오류를 잡을 수 있도록 하는 과정이다.
- 타입 시스템의 구조적 본질을 해치지 않고
객체 리터럴에 알 수 없는 속성을 허용하지 않는 기능
을 담당한다.
잉여 속성 체크가 적용되지 않는 경우
- 객체 리터럴이 아닌 경우
- 타입 단언문을 사용할 때
만약 잉여 속성 체크를 사용하고 싶지 않다면?
- 앞선 예제처럼 임시 변수를 사용하면 잉여 속성 체크를 건너뛸 수 있다.
- 혹은 인덱스 시그니처를 이용하여 타입스크립트가 추가적인 속성을 예상하도록 할 수 있다.
- 이런 방법이 데이터 모델링에 적절한지 여부는 추후(아이템 15)에 다룬다.
interface Options {
darkMode?: boolean;
[otherOptions: string]: unknown;
}
const o: Options = { darkmode: true }; // OK
출처
- 이펙티브 타입스크립트