일관성있는 별칭 사용하기

별칭(alias)을 남발해서 사용하면 제어 흐름을 분석하기 어렵습니다. TS에서도 마찬가지로 별칭을 신중하게 사용해야합니다. 그래야 코드를 잘 이해할 수 있고, 오류도 쉽게 찾을 수 있기 때문입니다.

interface Coordinate {
  x: number;
  y: number;
}

interface BoundingBox {
  x: [number, number];
  y: [number, number];
}

interface Polygon {
  exterior: Coordinate[];
  holes: Coordinate[][];
  bbox?: BoundingBox;
}

위와 같은 자료 구조가 있고, 이에 대해 아래와 같은 함수가 있다고 가정합시다. 현 시점에서 이는 타입에러도 없고, 잘 동작하지만 코드가 반복되는 부분이 존재합니다.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  if (polygon.bbox) {
    if (pt.x < polygon.bbox.x[0] || pt.x > polygon.bbox.x[1] ||
        pt.y < polygon.bbox.y[1] || pt.y > polygon.bbox.y[1]) {
      return false;
    }
  }

  // ... more complex check
}

여기서 중복되는 부분들을 없애기 위해 별도로 box라는 이름의 별칭으로 polygon.bbox를 참조하도록 하는 방법을 사용할 수 있습니다.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  const box = polygon.bbox;
  if (box) {
    if (pt.x < box.x[0] || pt.x > box.x[1] ||
        pt.y < box.y[1] || pt.y > box.y[1]) {  // OK
      return false;
    }
  }
  // ...
}

사실 제일 이상적인 방법은 Destructuring(비구조화) 문법을 통해 bbox라는 일관된 이름을 사용하도록 하는 것입니다. 이를 적용하면 아래와 같아집니다.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  // Destructuring을 통해 일관된 이름을 사용할 수 있도록 합니다.
  const { bbox } = polygon;
  if (bbox) {
    const { x, y } = bbox;
    if (pt.x < x[0] || pt.x > x[1] ||
        pt.y < x[0] || pt.y > y[1]) {
      return false;
    }
  }
  // ...
}

객체 프로퍼티에 직접 접근하지 않고 별도의 지역변수로 분리해낸다는 점은 타입 관점에서 더 안전합니다. 아래와 같이 프로퍼티를 직접 참조하는 경우 기존에 좁혀졌던 타입이 함수 호출 등으로 신뢰할 수 없는 상태가 될 수 있기 때문입니다.

const deletePolygonBox = (polygon: Polygon) => {
    polygon.bbox = undefined;
}

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
    polygon.bbox  // Type is BoundingBox | undefined
    if (polygon.bbox) {
    polygon.bbox  // Type is BoundingBox
    deletePolygonBox(polygon); // polygon.bbox = undefined;
    polygon.bbox  // Type is BoundingBox
    }
}

단, 지역변수로 분리한 경우 기존 프로퍼티 polygon.bboxbbox가 항상 같음을 보장할 수 없다는 점에 주의해야합니다.

const resetPolygonBox = (polygon: Polygon) => {
    polygon.bbox = {
      x: [0, 0],
      y: [0, 0],
    };
}

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
    const { bbox } = polygon;
    if (bbox) {
    resetPolygonBox(polygon); 
    // 이제 bbox와 polygon.bbox는 동일하지 않습니다.
    // ...
  }
}