TypeScriptに入門中ですが、まだまだ書き方に戸惑うことばかり。。
そこでTypeScript(JavaScript)の関数・高階関数と、それを使ったDependency Injection(DI)について簡単に整理してみます。
TS/JSでは、関数が「第一級オブジェクト」となっているそうです。
初耳ですが、関数を他の型と同じく変数に代入することができる、という意味らしいです。
変数の型にはstring,number,objectなどがありますが、関数の型ももちろんあって、
(引数名:引数の型) => 返り値の型
となります。アロー関数の書き方と似ていますね、
例えば2つの数字を受け取ってその合計を返す関数の型は
(a:number,b.number) => number
となり、型注釈をつけた実際の関数は
type addNumberType = (a:number,b.number) => number const addNumber:addNumberType = (a:number,b.number) => a + b
となります。関数の型に引数の名前まで入っているのが面白いですね。
そしてaddNumberは関数が入った変数となります。
変数に関数を簡単に入れられるので、TS/JSでは、関数を引数にしたり、関数を別の関数の返り値にしたりすることがよく行われます。
例えば配列のmapには、それぞれの要素をどう処理するかの関数を()の中に書きますが、それを変数で渡す感じです。
const mapFunc = (numberArray:number[], mapFunc:(x:number) => number) => // 配列と関数を受け取って console.log(numberArray.map(mapFunc)) // それをmapする
const arr1 = [1,2,3] const func1 = (x:number) => x * 2; mapFunc(arr1,func1) // [2,4,6]
別の例で、ある数字を与えると環境変数から取ってきた数字にそれを加える、という関数を考えてみます。
環境変数から数字を取ってくる関数は、環境に左右されずにテストを行うためにも、まずはモックを作ります。
const getNumberFromConfigMock = ():number => 15;
型注釈をつけると下記に書き換えられます
type getNumberFromConfig = () => number const getNumberFromConfigMock:getNumberFromConfig = () => 15;
では、この関数と、それに足す数を引数に取るメインの関数を書いてみましょう
const addConfigNumber = (getConfigFunc:getNumberFromConfig, add:number) => { const configNumber = getConfigFunc() return configNumber + add } // 使う時には2つの引数(関数と足す数)を入れる const result = addConfigNumber(getNumberFromConfigMock,5))
addConfigNumberを使う時に、毎度毎度getNumberFromConfig型の関数を渡すのは無駄ですし、書き方も冗長になります。
なので、予め環境変数から取得する関数がセットアップ済みの関数を作ってみましょう。
それには、「カリー化」を行います。
const addConfigNumberHO = (getConfigFunc:getNumberFromConfig) => { const addFunc = (add: number) => { const configNumber = getConfigFunc() return configNumber + add } return addFunc }
addConfigNumberHOは、getNumberFromConfig型の関数を受け取って、別の関数を返す、という作りになりました。
別の関数の中身では、渡されたgetConfigFuncを使って環境変数から値を受け取り、渡された値を加算しています。
関数を受け取って関数を返す、「高階関数」となりました。
これを使う際には、まずはgetNumberFromConfigMockを与えます。
これを、Dependency Injection(依存性注入)と呼びます。
const addConfigNumber = addConfigNumberHO(getNumberFromConfigMock)
一度DIをしてしまえば、あとは残りの引数(足す数)を与えるだけで使うことができます。
console.log(addConfigNumber(1)) // 16 console.log(addConfigNumber(5)) // 20 console.log(addConfigNumber(10)) // 25
なお、カリー化された後も、ワンライナーで引数を2つ与える形でも使用できます
console.log(addConfigNumberHO(getNumberFromConfigMock)(10)) // 10