Typescriptにおける関数の管理
解決したいこと
関数を書いていくにあたって複数の実現方法が考えられるが諸々込みで考えたときに、どういうケースで何を使うべきか、明らかにしたい。
対象
- static method
- exported function
- 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;
}
仮説
まず、大前提として、classを利用するかどうかの2つは明確に異なる。
classを利用しない上で、functionを使うべきかarrow functionを使うべきか、の判断が入る。
classを使うべきか
classを使うメリット
- 擬似的な名前空間を用意できるので、名前の衝突が発生しにくい
- クラス名がラベルのような役割を担えるので、命名が楽になる可能性がある
classを使うデメリット
- 複数の関数を持っちゃうと本質的に必要なモノ以外もimportされちゃう (= Treeshaking周りで不利かも)
- Java脳だとstatic ??? メモリが・・・!ってなっちゃう
function vs arrow function
arrow functionを使うメリット
- スマートに書ける
- callback等で使うarrow functionと同じノリで書けるので統一感が出る
arrow functionを使うデメリット
- モックがしんどいときがある
- 関数なのか値なのかパッと見で判断できないときがある
- 巻き上げが発生しないので宣言順序を気にする必要がある
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));
一般的なstatic vs function議論
やはりメリットは名前空間として機能させることと、デメリットはtreeshakingの恩恵を受けられないことと結論付けられている。
ということは過度に抽象的なクラスに種々様々な関数を詰め込まなければメリットだけを享受できそうではある。
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))
一般的な議論
いくつかのユースケースでArrow Functionが代替にならないと書かれているがその程度。
結論
規模が大きくなるプロジェクトでは、Static Methodを使うと名前の衝突を避けられるので積極的に使って良いかもしれない。
ただし、使う際は肥大化しないように注意する必要がある。
規模が小さいプロジェクトではそこまで意識しなくて良さそう。
コーディングの楽さを考えるとわざわざ名前空間を用意するのも面倒なので関数を使えば良さそう。
オブジェクトの中に持たせたり、クラスの中で持たせて自身にアクセスする必要がある場合は通常のfunctionを使わざるを得ない。
が、そういうケースでも無ければArrow Functionで問題なさそう。
特に名前付きexportをする程度であればどちらでも大差ない。