반응형
아이템 21 - 타입 넓히기
넓히기 (widening)
- 런타임에 모든 변수는 유일한 값을 가진다
- 타입스크립트가 작성된 코드를 체크하는 정적 분석 시점에, 변수는 '가능한' 값들의 집합인 타입을 가진다
- 상수를 사용해 변수를 초기화할 때 타입을 명시하지 않으면 타입 체커는 타입을 결정해야 한다
- 즉, 단일 값을 가지고 할당 가능한 값들의 집합을 유추해야 한다
interface Vector3 { x: number; y: number; z: number; }
function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') {
return vector[axis];
}
let x = 'x'
let vec = {x: 10, y: 20, z: 30}
getComponent(vec, x);
// 'string' 형식의 인수는 '"x" | "y" | "z"' 형식의 매개변수에 할당할 수 없습니다.
변수 x
는 넓히기에 의해 string
으로 추론되어 할당이 불가능해짐
const mixed = ['x', 1] // (string|number)[]
넓히기 과정을 제어하는 방법
const
로 선언하기- 객체는 선언시 모양을 기준으로 추론되기 때문에, 한번에 만들어야 함
- 명시적 타입 구문 제공
const v: { x: 1|3|5 } = {
x: 1,
} // 타입이 { x: 1|3|5 }
- 타입 체커에 추가적인 문맥 제공
- 매개변수에 추가적인 문맥 전달
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; redaonly y: 2; }
아이템 22 - 타입 좁히기
타입스크립트는 일반적으로 조건문에서 타입을 좁히는 데 매우 능숙합니다
- null 체크
- 분기문에서 예외를 던지거나 반환
- instanceof
- 속성 체크 ('a' in ab)
- Array.isArray 같은 일부 내장 함수로도 가능
function contains(text: string, terms: string | string[]) {
const termList = Array.isArray(terms) ? terms : [terms];
termList; // 타입이 string[]
}
태그된/구별된 유니온
- 명시적 '태그'를 붙여서 타입 좁히기
interface UploadEvent { type: 'upload' }
interface DownloadEvent { type: 'download' }
type AppEvent = UploadEvent | DownloadEvent;
function handleEvent(e: AppEvent) {
switch(e.type) {
case 'download':
e; // 타입이 DownloadEvent
break;
case 'upload'
e; // 타입이 UploadEvent
break;
}
}
사용자 정의 타입 가드
- 타입 식별을 돕기 위한 커스텀 함수
function isInputElement(el: HTMLElement): el is HTMLInputElement {
return "value" in el;
}
function getElementContent(el: HTMLElement) {
if (isInputElement(el)) {
el; // 타입이 HTMLInputElement
return el.value;
}
el; // 타입이 HTMLElement
return el.textContent;
}
- 반환타입이 true일 경우 타입 체커에게 매개변수의 타입을 좁힐 수 있다고 전달
- undefined가 올 가능성이 있을 때, 타입 가드를 사용하면 타입 좁히기 가능.
아이템 23 - 한꺼번에 객체 생성하기
// 객체는 한번에 생성하는 것이 좋음
const pt: Point = {
x: 3,
y: 4,
}
// 확장시 스프레드 연산자 활용
const namedPoint = { ...pt, ...id }
// 조건부 속성 확장 시
아이템 24 - 일관성 있는 별칭 사용하기
- 별칭을 사용해서 원래의 속성값을 변경가능
- 별칭을 남발해서 사용하면 제어 흐름을 분석하기 어려움
- 객체 비구조화를 통해 일관된 이름을 사용하는 것이 좋음
- 주의점
- 타입 경계에 null값을 추가하는 것이 좋음
- 없는 속성값은 나타내기
- 주의점
- 타입스크립트는 함수가 타입 정제를 무효화하지 않는다고 가정하지만 실제로는 무효화될 가능성이 있음
- 속성보다는 지역 변수를 사용하는 것이 타입 정제를 더욱 믿을 수 있음
아이템 25 - 비동기 코드에는 콜백 대신 async 함수 사용하기
Promise
와 async/await
을 콜백 대신 사용하는 이유
- 코드를 작성하기 쉬움
- 타입을 추론하기 쉬움
Promise
대신 async/await
을 사용해야 하는 이유
- 일반적으로 더 간결하고 직관적
async
함수는 항상 프로미스를 반환하도록 강제- 값을 통일하는데 도움이 됨
- 또 다른 프로미스로 래핑되지 않기 때문에 타입 정보가 명확함
아이템 26 - 타입 추론에 문맥이 어떻게 사용되는지 이해하기
문맥을 고려한 타입 추론에는 가끔 이상한 결과나 나올 수 있음
type Language = 'JavaScript' | 'TypeScript' | 'Python';
function setLanguage(language: Language) {}
setLanguage('JavaScript') // 정상
let language = 'JavaScript';
setLanguage(language); // 'string' 형식의 인수는 'Language' 형식의 매개변수에 할당될 수 없습니다.
해결 방법
- 타입 선언에서
language
의 가능한 값을 제한
- 오타 오류를 표시해주는 장점이 있음
let language: Language = 'JavaScript';
setLanguage(language) // 정상
language
를 상수로 만듦
const language = 'JavaScript';
튜플 사용 시 주의점
function panTo(where: [number, number])
panTo([10, 20]) // 정상
const loc = [10, 20];
panTo(loc); // 'number[]' 형식의 인수는 '[number, number]' 형식의 매개변수에 할당될 수 없습니다.
해결 방법
- 타입 선언 제공
const loc: [number, number] = [10, 20]
- 상수 문맥 제공
const loc = [10, 20] as const
- 타입 정의에 실수가 있다면 오류는 호출부에서 발생하므로 주의해야 함
readonly
구문 추가
function panTo(where: readonly [number, number]) {}
const loc = [10, 20] as const;
panTo(loc);
객체 사용 시 주의점
튜플과 유사
콜백 사용 시 주의점
- 타입스크립트는 콜백의 매개변수 타입을 추론하기 위해 문맥을 사용
- 가능한 전체 함수 표현식에 타입 선언을 적용하는 것이 좋음
아이템 27 - 함수형 기법과 라이브러리로 타입 흐름 유지하기
내장 함수와 유틸리티 라이브러리를 사용해야 하는 이유
- 타입 흐름 개선
- 가독성 증가
- 명시적 타입 구문의 필요성을 줄임
아이템 28 - 유효한 상태만 표현하는 타입을 지향하기
- 타입을 설계할 떄, 어떤 값을 포함하고 어떤 값을 제외할지는 신중하게 생각해야 함
- 유효한 상태에서만 표현하는 타입을 지향해야 함
- 무효한 상태 예시 -
isLoading
과error
가 동시에 설정되는 상태
- 무효한 상태 예시 -
아이템 29 - 사용할 때는 너그럽게, 생성할 때는 엄격하게
- 함수 시그니처에서 매개변수는 타입의 범위가 넓어도 되지만, 결과를 반환할 때는 타입의 범위가 더 구체적이어야 함
- 매개변수와 변환 타입의 재사용을 위해서 기본 형태와 느슨한 형태를 도입하는 것이 좋음
아이템 30 - 문서에 타입 정보를 쓰지 않기
- 주석에 타입 정보 적지 않기 / 모순 발생
- 타입이 명확하지 않은 경우, 변수명에 단위 정보를 포함하는 것을 고려해야 함
출처: 이펙티브 타입스크립트