
요약 : 매개변수는 너그럽게 받고, 반환값은 엄격하게 반환하라
interface CameraOptions {
center?: LngLat;
zoom?: number;
bearing?: number;
pitch?: number;
}
type LngLat =
{ lng: number; lat: number; } |
{ lon: number; lat: number; } |
[number, number];
type LngLatBounds =
{northeast: LngLat, southwest: LngLat} |
[LngLat, LngLat] |
[number, number, number, number];
declare function setCamera(camera: CameraOptions): void;
declare function viewportForBounds(bounds: LngLatBounds): CameraOptions;
위와 같은 카메라에 대한 타입이 있다. 카메라 설정은 일부 값을 건드리지 않으면서 다른 값을 받을 수 있도록 CameraOptions는 모두 선택적이다.
카메라의 경계박스의 뷰포트를 계산하는 viewportForBounds는 bounds 값을 받아 카메라 옵션을 반환한다.
LngLat, LngLatBounds 모두 union을 이용해 타입을 넓힘으로써 다양한 매개변수를 받고있다. 즉 사용자가 사용하기에 너그럽게 생성했다.

interface LngLat { lng: number; lat: number; };
type LngLatLike = LngLat | { lon: number; lat: number; } | [number, number];
interface Camera {
center: LngLat;
zoom: number;
bearing: number;
pitch: number;
}
//omit은 ~를 제외하고 부분적으로 받는다는 뜻이다. 즉 camera의 center를 제거한 속성들만 partial로 받겠다는 뜻
interface CameraOptions extends Omit<Partial<Camera>, 'center'> {
center?: LngLatLike;
}
type LngLatBounds =
{northeast: LngLatLike, southwest: LngLatLike} |
[LngLatLike, LngLatLike] |
[number, number, number, number];
declare function setCamera(camera: CameraOptions): void;
declare function viewportForBounds(bounds: LngLatBounds): Camera;


코드가 중복되지만 이런식으로 명시적 작성을 해줘도 된다는데, 위 코드가 더 멋있는 것 같다.
요약 1 : 주석으로 설명하기 보다는, 코드로 한눈에 딱 알아볼 수 있게 작성하기
=> 주석은 나중에 유지보수하기 귀찮기도 하고 자동으로 변경되지도 않는다.
요약 2 : 특정 매개변수를 설명하고 싶다면 JSDoc의 @PARAM 구문을 사용하자
type Color = { r: number; g: number; b: number };
/**매개변수가 있을 때는 특정페이지의 전경색을 반환합니다.. 보다는*/
function getForegroundColor(page?: string): Color {
// COMPRESS
return page === 'login' ? {r: 127, g: 127, b: 127} : {r: 0, g: 0, b: 0};
// END
}

이처럼 JSDoc의 param을 이용하는게 더좋다. parameter에 댔을때 관련설명도 보여주는 좋은 기능이다.
정보의 모순이 발생할 수도 있기때문에 주석과 변수명에 타입정보를 적는건 피해야 한다.
타입이 명확하지 않을 떈, 변수명에 단위 정보를 포함하는것을 고려해라
(timeMs, temperatureC 등등)
요약 1 : null값을 잘 걸러줘라, undefined를 포함하게 하지 말아라!
요약 2 : 데이터를 받는 비동기함수의 경우 받을때는 null값을 허용하되 반환할 때는 null값이 아니게 반환하라
요약 3 : null값인 변수와 아닌 변수가 섞이게 하지 말아라, 골치아파진다.
interface UserInfo { name: string }
interface Post { post: string }
declare function fetchUser(userId: string): Promise<UserInfo>;
declare function fetchPostsForUser(userId: string): Promise<Post[]>;
class UserPosts {
user: UserInfo | null;
posts: Post[] | null;
constructor() {
this.user = null;
this.posts = null;
}
async init(userId: string) {
return Promise.all([
async () => this.user = await fetchUser(userId),
async () => this.posts = await fetchPostsForUser(userId)
]);
}
getUserName() {
// ...?
}
}
class UserPosts {
user: UserInfo;
posts: Post[];
constructor(user: UserInfo, posts: Post[]) {
this.user = user;
this.posts = posts;
}
static async init(userId: string): Promise<UserPosts> {
const [user, posts] = await Promise.all([
fetchUser(userId),
fetchPostsForUser(userId)
]);
return new UserPosts(user, posts);
}
getUserName() {
return this.user.name;
}
}
개선한 코드, init을 호출하면 객체가 반환된다. (js엔 init 내장함수가 없다.)user와 posts가 초기화 된후 UserPosts가 반환된다. 위와 같이 코드를 짜면 null로 초기화 될 이유 없어서 더 좋다고 한다.