Item 29 사용할 때는 너그럽게, 생성할 때는 엄격하게

Summary

  • 함수의 매개변수는 타입의 범위가 넓어도 되지만, 결과를 반환할 때는 일반적으로 타입의 범위가 더 구체적이어야 한다.

  • 매개변수와 반환 타입의 재사용을 위해서 느슨한 형태(매개변수 타입)와 기본 형태(반환 타입)를 도입하는 것이 좋다.

예를 들어...

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
}
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
  • Camera가 너무 엄격하므로 조건을 완화하여 느슨한 CameraOptions를 매개변수로 받도록 했다. 유틸 타입에 대해서는 Item 14 타입 연산과 제너릭 사용으로 반복 줄이기 참고

  • LngLatLike, LngLatBounds를 보면 다양한 형태를 지원하고 있는데 이렇게 느슨하게 허용하는 게 좋긴 하지만 조합이 너무 많아지면 좋은 설계는 아니다. 하지만 다양한 타입을 허용해야만 하는 라이브러리의 타입 선언을 작성하고 있다면 어쩔 수 없다.

My Thoughts

예를 들어 사용자 정의 타입 가드(type guard) 함수를 만든다고 하면, 가능하면 좁고 특정한 타입보다는 string 또는 unknown 같은 타입이 해당 타입인지 검증하는 로직으로 짜는 것이 좋을 것 같다.

Last updated