l

o

a

d

i

n

g

.

.

.

TypeScriptを自分流環境で構築して学習する話 #3. 関数型スタイルとジェネリクスに慣れる

2026/01/11

Javascript プログラミング 学習

t f B! P L
TSというよりモダンJSは、「関数でデータを変換していく書き方」と非常に相性が良いんです。 状態を変更せず、入力→処理→出力 を明確にする思想ですね。 この"関数型スタイル"と、「データ型を後から注入する仕組み」の"ジェネリスク(Generics)"という、型の再利用・抽象化ができるスタイルについて学んでみます。 最後に、「関数を返す関数」「関数を受け取る関数」という"高階関数(Higher-Order Functions)"についても説明するので、覚えるとTSマスターに近づきますよ。

関数型スタイル

命令型

let arr = [1,2,3] for(let i=0; i<arr.length; i++){ arr[i] = arr[i] * 2 }

関数型

const arr = [1,2,3] const result = arr.map(x => x * 2)

特徴

・副作用を減らす ・型推論が活きる ・関数を組み合わせやすい

関数型×TSが最強になる理由

関数型パターンは入力と出力が明確なので、TSの型システムと相性が良いんです。 const add = (a: number, b: number): number => a + b 関数の境界に型があるので、「意図が伝わる」コードになります。 また、条件分岐や例外処理が減るため、「型がロジックを導く」ことができます。 要するに、コメントいらずの効率が良いコードになるという事ですね。

ジェネリクス

型を固定した関数

この状態では、stringしか使えません。 function wrapString(value: string): string[] { return [value] }

ジェネリクス型

関数の”仕様”は同じだけど、扱う型は呼び出す側が決めることができます。 function wrap<T<(value: T): T[] { return [value] } wrap(1) // number[] wrap("hello") // string[] wrap({id: 1}) // {id: number}[]

関数型 × Generics=TSらしい書き方

配列をフィルタする関数

function filter<T>(arr: T[], predicate: (value: T) => boolean): T[] { return arr.filter(predicate) } const nums = filter([1,2,3,4], (n) => n > 2) // number[] const words = filter(["a","bbb","cc"], (w) => w.length > 1) // string[]

解説

・型の制約は関数定義で行う。 ・型の選択は使う側で行う。  → 拡張性・再利用性が高いコードになる。 ・multiply(2) を実行すると「新しい関数」が返り、値を受け取る。 → 関数を返すので"関数を返す関数"

Higher-Order Functions(高階関数)

例:部分適用(Partial Application)

const multiply = (a: number) => (b: number) => a * b const double = multiply(2) double(5) // -> 10 ポイント ・関数は値として扱う。 ・副作用(Side effect)を最小化することができる。 ・型は境界(関数の入出力)に置く。 ・処理は小さい関数に分解して、その後に合成する。 ・ジェネリクスを使うと、再利用性と柔軟性を上げることができる。

ジェネリクスを使ってより効率化

const identity = <T>(value: T): T => value ポイント ・ジェネリクスにより、型が呼ぶ側で決まる identity(10) // -> number型 identity("hello") // -> string型 identity([1,2,3]) // -> number[]型

関数を受け取る関数

const applyTwice = <T>(fn: (v: T) => T, value: T): T => fn(fn(value)) applyTwice(x => x + 1, 5) // -> 7 ポイント ・fn が "関数" それを引数で受け取るので → 関数を受け取る関数(Higher-Order Function)

関数の分類まとめ

普通の関数

(a: number) => a + 1 型 : 値 → 値 入力に対して値を返す単純な関数

関数を受け取る関数(高階関数)

arr.map(fn) 型 : 関数 → 値 or 関数 関数を引数として受け取る

関数を返す関数

multiply(2)(5) 型 : 関数 呼び出すと別の関数が戻る

両方(関数を受け取って関数を返す)

compose(fn1, fn2) 型 : 関数 FPライブラリ系に多い
multiply → 関数を返す関数(部分適用・カリー化) identity → ジェネリクス付き普通の関数 applyTwice → 関数を受け取る関数(高階関数)
この3つは「見た目が似てても役割が全然違う」という事を理解しよう!

あとがき

高階階層は、モダンJSでも下記のように書けます。 var a = a => b => a*b a(2)(3) > 6 これに型宣言と、独特のジェネリクス記述をすると、TSっぽさが出るということですね。 でも、ジェネリクスって「型を呼び出しが決める」という事だけど、 「シンプルに書けるモダンJSと同じじゃね?」と思ったのは自分だけでしょうか? でも、ジェネリクスは、次のように書くことでより安定したコードが表示前にエラーを見つけることができるようです。 function map<T, R>(arr: T[], fn: (v: T) => R): R[] { return arr.map(fn); } map([1, 2, 3], x => x * 2) // number[] ✔ map(["a","b"], x => x.toUpperCase()) // string[] ✔ map([1,2,3], x => x.toUpperCase()) // compile error ❌ (事前に防げる) どうやら、このジェネリクスに慣れることが、TSマスターに近づくポイントかもしれませんね。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ