Open6

Typescriptにおける関数の管理

anizozinaanizozina

解決したいこと

関数を書いていくにあたって複数の実現方法が考えられるが諸々込みで考えたときに、どういうケースで何を使うべきか、明らかにしたい。

対象

  1. static method
  2. exported function
  3. exported arrow function

static method

export class DateUtil {
  static addDate ( date: Date, num: number ) {
    const d = new Date(date); 
    d.setDate(d.getDate() + num);
    return d;
  }
}

exported function

export function addDate(date: Date, num: number) {
  const d = new Date(date);
  d.setDate(d.getDate() + num);
  return d;
}

exported arrow function

export const addDate2 = (date: Date, num: number) => {
  const d = new Date(date);
  d.setDate(d.getDate() + num);
  return d;
}
anizozinaanizozina

仮説

まず、大前提として、classを利用するかどうかの2つは明確に異なる。
classを利用しない上で、functionを使うべきかarrow functionを使うべきか、の判断が入る。

classを使うべきか

classを使うメリット

  • 擬似的な名前空間を用意できるので、名前の衝突が発生しにくい
  • クラス名がラベルのような役割を担えるので、命名が楽になる可能性がある

classを使うデメリット

  • 複数の関数を持っちゃうと本質的に必要なモノ以外もimportされちゃう (= Treeshaking周りで不利かも)
  • Java脳だとstatic ??? メモリが・・・!ってなっちゃう

function vs arrow function

arrow functionを使うメリット

  • スマートに書ける
  • callback等で使うarrow functionと同じノリで書けるので統一感が出る

arrow functionを使うデメリット

  • モックがしんどいときがある
  • 関数なのか値なのかパッと見で判断できないときがある
  • 巻き上げが発生しないので宣言順序を気にする必要がある
anizozinaanizozina

Class (Static 関数)について

ref. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static

気になったこと

  • classなので当然のように拡張ができる
    • つまり、Static関数を持つclassをモックの対象にしたいときに上書きできる、、、と思ったけど冷静に考えてinstanceをわたしているわけじゃないから、テストの観点ではあまりメリットはなさそう
    • 良し悪しは置いておいて、差し替え自体はできる
export class DateUtil {
  static addDate(date: Date, num: number) {
    const d = new Date(date);
    d.setDate(d.getDate() + num);
    return d;
  }
}

// "2022-12-02T00:00:00.000Z"
console.log(DateUtil.addDate(new Date('2022-12-01T00:00:00Z'), 1));
DateUtil.addDate = (date: Date) => {
  return date;
}
// "2022-12-01T00:00:00.000Z"
console.log(DateUtil.addDate(new Date('2022-12-01T00:00:00Z'), 1));
anizozinaanizozina

arrow function vs function

Arrow Functionとは

ref. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

概要にあるとおりでいくつかの機能が削ぎ落とされているものになる。
実際、Dev Tools等で確認してみるとわかる。

const arFn = () => console.log('hoge')
function fn () { console.log('fuga') }

// ['length', 'name']
console.log(Object.getOwnPropertyNames(arFn))

// ['length', 'name', 'arguments', 'caller', 'prototype']
console.log(Object.getOwnPropertyNames(fn))

一般的な議論

ref. https://stackoverflow.com/questions/34361379/are-arrow-functions-and-functions-equivalent-interchangeable

いくつかのユースケースでArrow Functionが代替にならないと書かれているがその程度。

anizozinaanizozina

結論

規模が大きくなるプロジェクトでは、Static Methodを使うと名前の衝突を避けられるので積極的に使って良いかもしれない。
ただし、使う際は肥大化しないように注意する必要がある。

規模が小さいプロジェクトではそこまで意識しなくて良さそう。
コーディングの楽さを考えるとわざわざ名前空間を用意するのも面倒なので関数を使えば良さそう。
オブジェクトの中に持たせたり、クラスの中で持たせて自身にアクセスする必要がある場合は通常のfunctionを使わざるを得ない。
が、そういうケースでも無ければArrow Functionで問題なさそう。

特に名前付きexportをする程度であればどちらでも大差ない。