TL;DR

  • 値のエイリアスは、TypeScript による型の絞り込みを妨げることがある。エイリアスを作ったら一貫してそれを使う。
  • 分割代入を使うと、名前に一貫性が生まれ、コードも簡潔になる。

内容

エイリアスとは

エイリアスとは、同じ値を別の名前で参照することである。参照型やポインタに近いイメージである。

const place = { name: "New York", latLng: [41.6868, -74.2692] }
const loc = place.latLng

エイリアスを通じてプロパティを変更すると、元の値にも反映される。

loc[0] = 0
place.latLng // [0, -74.2692]

TypeScript でのエイリアス

TypeScript では、エイリアスが型の絞り込みを妨げることがあるので、エイリアスを導入したら一貫してそれを使う。次のコードでは、polygon.bbox で絞り込んでも、エイリアスの box は絞り込まれない。

const isPointInPolygon = (polygon: Polygon, pt: Coordinate) => {
  const box = polygon.bbox // box: BoundingBox | undefined
  if (polygon.bbox) {
    if (
      pt.x < box.x[0] ||
      pt.x > box.x[1] ||
      //     ---                --- 'box' is possibly 'undefined'
      pt.y < box.y[0] ||
      pt.y > box.y[1]
      //     ---                --- 'box' is possibly 'undefined'
    ) {
      return false
    }
  }
  // ...
}

このとき分割代入(const { bbox } = polygon)を使うと、変数名に一貫性が生まれ、コードも簡潔になる(項目41)。

注意点

関数にオブジェクトを渡すと、その関数がプロパティを削除する可能性がある。そのため、本来はプロパティの型の絞り込みが無効になりうる。しかし TypeScript は利便性を優先し、関数が型の絞り込みを無効にしないと仮定する。

polygon.bbox
//      ^? (property) Polygon.bbox?: BoundingBox | undefined
if (polygon.bbox) {
  polygon.bbox
  //      ^? (property) Polygon.bbox?: BoundingBox
  expandABit(polygon)
  polygon.bbox
  //      ^? (property) Polygon.bbox?: BoundingBox
}

この仮定が健全性を損なう原因になる(項目48)。

以上を踏まえると、ローカル変数による型の絞り込みを信頼したほうがよい。ただしこのままだと、変数と元のオブジェクトとで値が食い違う副作用が起こりうる。これが心配なら、関数には readonly なオブジェクトを渡すとよい(項目14)。

キーワード