3️⃣

[TypeScript UtilityTypes] ConstructorParameters

2024/01/13に公開

TypeScript入門メモ
[Utility Types] ConstructorParameters について

ConstructorParameters<Type>

公式ドキュメント
https://www.typescriptlang.org/docs/handbook/utility-types.html#constructorparameterstype

コンストラクタ関数型の型からタプル型または配列型を構築します。これにより、すべてのパラメータの型を持つタプル型が生成されます(ただし、Typeが関数でない場合はnever型になります)。

type T0 = ConstructorParameters<ErrorConstructor>;
// type T0 = [message?: string]

type T1 = ConstructorParameters<FunctionConstructor>;
// type T1 = string[]

type T2 = ConstructorParameters<RegExpConstructor>;
// type T2 = [pattern: string | RegExp, flags?: string]

class C {
  constructor(a: number, b: string) {}
}
type T3 = ConstructorParameters<typeof C>;
// type T3 = [a: number, b: string]

type T4 = ConstructorParameters<any>;
// type T4 = unknown[]
 
type T5 = ConstructorParameters<Function>; // error
// Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'.
// Type 'Function' provides no match for the signature 'new (...args: any): any'.
// type T5 = never

使い所

特にクラスのインスタンスを動的に作成する際や、クラスコンストラクタの型情報を再利用する際に役立てる。

createInstance 関数が User クラスと Product クラスの両方のインスタンスを生成するのに使用されている。
ConstructorParameters を用いることで、これらのクラスのコンストラクタに渡す引数の型が正しいことが保証され、型安全なインスタンス生成が可能になる。

このようなファクトリ関数は、クラスのインスタンスを動的に生成する必要がある場合や、依存関係注入フレームワーク、テストモックの作成など多様なシナリオで役立つはず。

// クラスの定義
class User {
  constructor(
    public name: string,
    public age: number,
    public isAdmin: boolean
  ) {}
}

// クラスの定義
class Product {
  constructor(public title: string, public price: number) {}
}

// 余談:ジェネリック型 T に new (...args: any[]) => T というコンストラクタシグネチャの制約を追加する
//      これにより、型不一致の引数が渡された場合にコンパイル時にエラーが発生する
function createInstance<T extends new (...args: any[]) => any>(
  classRef: T,
  ...args: ConstructorParameters<T>
): InstanceType<T> {
  return new classRef(...args);
}

// インスタンスの生成
const user = createInstance(User, "Alice", 30, true);
const product = createInstance(Product, "Table", 99.99);

Discussion