こんにちは!フロントエンド・スペシャリストの私です。日々、TypeScriptの型システムという名のジャングルで、愛と情熱を持ってコードを書いている仲間の一人です。
TypeScriptを学び始めると、`string`や`number`といった基本の型から始まり、配列やオブジェクト、そしてちょっとクセのある`any`や`unknown`なんかに出会って、「おっ、だいたい分かってきたぞ!」と楽しくなってくる頃ですよね。
でも、そこから一歩進んで、型を自在に操る「ユーティリティ型」の世界を覗いたとき、こんな壁にぶつかったりはしていませんか?
> 「なんか公式ドキュメントに難しい記号が並んでて、宇宙語に見える……」
> 「`ConstructorParameters
大丈夫ですよ、安心してください。今日ここで一緒に解きほぐしていけば、「なーんだ、そういうことね!」とスッキリ腑に落ちるはずです。一緒に優しく紐解いていきましょう!
—
そもそも「ConstructorParameters」ってどんなお道具?
身近な例えでお話ししますね。
例えば、あなたが街の家具屋さんで「オーダーメイドの椅子」を注文するシーンを想像してみてください。その椅子を作るためには、職人さんが「木の種類」「座面の高さ」「色」といういくつかの材料(引数)を必要としますよね。
プログラムの世界における「クラスのコンストラクタ」もこれと全く同じです。
クラス(設計図)から、実際に使えるオブジェクト(実物)を作るとき、`new MyClass(“木”, 45, “赤”)` のように、いくつかの材料を渡してあげます。
ここで、こんな疑問が湧き上がってきます。
「このクラスを作るために、一体どんな材料(引数)がいくつ必要なのか、型としてパッと取り出したいなぁ」
それを実現してくれる魔法の道具が、TypeScriptに備わっている `ConstructorParameters
こいつは、渡されたクラスの設計図(コンストラクタ)をじーっと観察して、「ふむふむ、必要な材料はこれとこれとこれだね!」と、その型をきれいなタプル(配列のようなもの)にして抜き出してくれる凄腕の職人さんなんですよ。
—
コードで見てみよう!お買い物の流れ
百聞は一見にしかず、実際にコードを書いてその動きを確かめてみましょう。エディタを開く準備はいいですか?
今回は、みんな大好き「お買い物カート」を管理するクラスを例にしてみますね。
// お買い物カートのクラス
class ShoppingCart {
// コンストラクタ(カートを作る時の初期設定)で、ユーザーIDと店舗名を受け取る
constructor(public userId: string, public storeName: string) {
console.log(`${storeName}で、ユーザー ${userId} のカートを作りました!`);
}
}
この `ShoppingCart` クラスを作るには、文字列の `userId` と `storeName` の2つの材料が必要です。
では、ここで `ConstructorParameters
// ConstructorParametersを使って、ShoppingCartのコンストラクタの引数の型を抽出する
type CartParams = ConstructorParameters
// さあ、CartParamsの中身はいったいどうなっているでしょうか?
// 正解は…… [userId: string, storeName: string] という「タプル型」になります!
おや、ここで `typeof ShoppingCart` という見慣れない書き方が出てきましたね。
クラスはあくまで「値(もの)」ではなく「型(設計図)」なので、`typeof` をつけることで「このクラスのコンストラクタの型ですよ」とTypeScriptに教えてあげているんです。
—
タプル型ってなぁに?(ちょっとおさらい)
ここで少しだけ立ち止まって、「タプル型」についておさらいしておきましょう。難しく考えなくて大丈夫です。
タプルとは、いわば「中身の順番と型が決まっているカプセル」のようなものです。
今回の `CartParams` で言えば、こんなルールになっています。
1. 1番目の要素は必ず `string` 型(ユーザーID)
2. 2番目の要素は必ず `string` 型(店舗名)
つまり、`[string, string]` という決まった並び順を保証してくれるカプセルなんですね。
—
これ、実際の開発でどうやって使うの?(実用例)
「なるほど、引数の型を取り出せるのは分かったけど、それの何がすごいの? 実務でどう使うの?」
そう思いますよね。ここが一番大切なポイントです!
例えば、「関数の実行を後回しにする(遅延実行)」や、「関数の引数をそのまま別の関数に横流しする(関数のラップ)」というテクニックで、この `ConstructorParameters` はものすごい真価を発揮します。
ちょっと実践的な例を見てみましょう。クラスのインスタンスを生成する「工厂(ファクトリー)関数」を作ってみます。
// さっきの ShoppingCart クラス
class ShoppingCart {
constructor(public userId: string, public storeName: string) {}
}
/
- クラスのコンストラクタと、その引数を受け取って、安全にインスタンスを生成するヘルパー関数
/
function createInstance
Ctor: T,
…args: ConstructorParameters
): InstanceType
// 新しいインスタンスを作って返す
return new Ctor(…args);
}
// 使い方:
// パラメータの型が完璧に推論されるので、間違った引数を渡すとエディタが怒ってくれます!
const myCart = createInstance(ShoppingCart, “user_12345”, “スーパーマーケット”);
// ❌ もし間違えて数や型を間違えると……
// const errorCart = createInstance(ShoppingCart, 12345, “本屋さん”); // 型エラー!1番目はstringじゃないとダメだよ!
おぉ……!どうですか、このスマートさ。
`ConstructorParameters
—
つまずきやすいポイントと、そっと寄り添うアドバイス
ここまで読んで、「便利そうだけど、やっぱり記号が多くて頭がパンクしそう……」と感じている方もいるかもしれません。
フロントエンドの現場で長年やってきた私から、つまずいたときの処方箋をそっとお渡ししますね。
1. 一度に全部理解しようとしない
TypeScriptの高度な型は、パズルのようなものです。最初は「ふーん、クラスの引数をスポッと抜き出す便利なマジックテープなんだな」くらいのふんわりしたイメージで十分ですよ。
2. エディタの補完機能をめちゃくちゃ頼る
VS Codeなどのエディタで、抽出した型の上にマウスカーソルを乗せてみてください(ホバー機能)。`[userId: string, storeName: string]` のように中身がパッと表示されるはずです。「お、ちゃんと抽出できてるな!」と目で見て確認しながら進めるのが、一番の近道です。
—
おわりに
今回は、`ConstructorParameters
最初はとっつきにくく感じるユーティリティ型ですが、その正体は「私たちの開発を助けてくれる心強い相棒」に過ぎません。焦らず、自分のペースでコードを書きながら、型がピタッとハマる瞬間の「おっ、気持ちいい!」をぜひたくさん味わってくださいね。
あなたのTypeScriptライフが、今日から少しでも楽しく、ワクワクするものになりますように。
それではまた、次回のフロントエンドの深掘り記事でお会いしましょう!

コメント