interface Layer {
layout: FillLayout | LineLayout | Pointlayout;
paint: FillPaint | LinePaint | PointPaint;
}
layout이 LineLayout 타입이면서 paint 속성이 FillPaint 타입인 것은 말이 되지 않는다. 이런 조합을 허용하면 오류가 발생하기 쉽고 인터페이스를 다루기가 어렵다.
interface FillLayer {
layout: FillLayout;
paint: FillPaint;
}
interface LineLayer {
layout: LineLayout;
paint: LinePaint;
}
interface PointLayer {
layout: PointLayout;
paint: PointPaint;
}
type layer = FillLayer | LineLayer | PointLayer;
이런 형태로 Layer을 정의하면 layout과 paint 속성이 잘못된 조합으로 섞이는 경우를 방지할 수 있다.
interface Person {
name: string;
// 둘 다 동시에 있거나 동시에 없음
placeOfBirth?: string;
dateOfBirth?: Date;
}
👍 더 좋은 설계방법
interface Person {
name: string;
birth?: {
place: string;
date: Date;
};
}
유니온의 인터페이스보다 인터페이스의 유니온이 더 정확하고 타입스크립트가 이해하기도 좋다
string 타입의 범위는 매우 넓기 때문에 string 타입으로 변수를 선언하려 한다면, 혹시 그보다 더 좁은 타입이 적절하지는 않을지 검토해 봐야 한다.
👎 string 타입이 남발된 코드
interface Album {
artist: string;
title: string;
releaseDate: string; // YYYY-MM-DD
recordingType: string; // "live" 또는 "studio"
}
타입 체커를 통과 하더라도 releaseDate에 'August 17th, 1959' 같은 다른 날짜 형식, recordingType에 Live와 같은 대문자오타, 매개변수의 순서 오류가 발생할 수 있다.
✔️ 타입의 범위를 좁혀 개선한 코드
type RecordingType = "studio" | "live";
interface Album {
artist: string;
title: string;
releaseDate: Date;
recordingType: RecordingType;
}
👍 장점
✔️ 객체의 속성 이름을 함수 매개변수로 받을 때는 string보다 keyof T를 사용하는 것이 좋다
function pluck(records: any[], key: string): any[] {
return records.map((r) => r[key]);
}
function pluck<T>(records: T[], key: keyof T): T[keyof T][] {
return records.map((r) => r[key]);
}
pluck(albums, "releaseDate"); // type: (string | Date)[] 👎 적절한 타입이 아님
function pluck<T, K extends keyof T>(records: T[], key: K): T[K][] {
return records.map((r) => r[key]);
}
pluck(albums, "artist"); // type : string[]
pluck(albums, "releaseDate"); // type: Date[] 👍 적절한 타입