본문 바로가기

IT

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

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

 

[수정 전]

declare function setCamera(camera: CameraOptions): void; // 매개변수 타입의 범위를 넓게 해야한다.
declare function viewportForBounds(bounds: LngLatBounds): CameraOptions; // 매개변수 타입의 범위를 넓게 해야한다.

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];


function focusOnFeature(f: Feature) {
    const bounds = clacBoundBox(f);
    const camera = viewportForBounds(bounds);
    setCamera(camera);
    const {center: {lat, lng}, zoom} = camera; // 에러) 반환 타입의 범위가 넓으면 반환 받을때 어려움을 겪는다.
}

 

[수정 후]

interface LngLatFix {
    lng: number;
    lat: number;
};

type LngLatLike = LngLatFix | { lon: number; lat: number} | [number, number] ;

interface Camera {
    center: LngLatFix;
    zoom: number;
    bearing: number;
    pitch: number;
}

interface CameraOptionsFix extends Omit<Partial<Camera>, 'center'> {
    center?: LngLatLike;
}

type LngLatBoundsFix =
    { northeast: LngLatLike, southwest: LngLatLike} |
    [LngLatLike, LngLatLike] |
    [number, number, number, number];

declare function setCameraFix(camera: CameraOptions): void;
declare function viewportForBoundsFix(bounds: LngLatBoundsFix): Camera;

function focusOnFeature2(f: Feature) {
    const bounds = clacBoundBoxFix(f);
    const camera = viewportForBoundsFix(bounds);
    setCamera(camera);
    const {center: {lat, lng}, zoom} = camera; // 정상) 반환 타입은 좁혀진 상태로 하는것이 유리하다.

* 보통 함수의 매개변수 타입은 반환 타입에 비해 넓은 범위의 타입을 받도록 한다.

* 옵션 속성과 유니온 타입은 반환 타입보다는 매개변수 타입에 적용하는것이 일반적이다.

* 매개변수와 반환 타입의 재사용을 위해서 base 구조(strict 한)와 느슨한 형태를 함께 도입하는 것을 추천한다.