JavaScriptで名前付き引数と似たようなことをする

こんにちは、フロントエンドエンジニアのうじた(@uji_t4)です。 今回はコードレビュー中にphpの名前付き引数について教えてもらったので、JavaScriptで似たようなことが出来ないか調べてみました。

名前付き引数とは

名前付き引数とは、位置ではなく名前ベースで引数を渡すことができる機能です。 Pythonではキーワード引数と言うみたいです。 以下のように使います。

名前付き引数を使った場合

<?php

function greet($first_name, $last_name, $age) {
    return "Hello, $first_name $last_name. You are $age years old.";
}

// 引数の名前を指定して値を渡す
echo greet(age: 30, last_name: "Doe", first_name: "John");  
// Output: Hello, John Doe. You are 30 years old.

?>

名前付き引数を使っていない場合

<?php

function greet($first_name, $last_name, $age) {
    return "Hello, $first_name $last_name. You are $age years old.";
}

// 引数を順番に渡す
echo greet("John", "Doe", 30);  
// Output: Hello, John Doe. You are 30 years old.

?>

名前ベースで引数を渡すことができるため、任意の順番で渡せるようになります。 また、引数の意味が明確になるため、コードの可読性も向上します。

この機能はJavaScriptの言語仕様にはないみたいなので、似たようなことをやっていきたいと思います。

JavaScriptの場合

JavaScriptは以下のコードのように引数でオブジェクトを受け取ることが出来ます。

function greet(params) {
    return `Hello, ${params.firstName} ${params.lastName}. You are ${params.age} years old.`;
}

// オブジェクトを使用して引数を渡す
console.log(greet({age: 30, lastName: "Doe", firstName: "John"}));  
// Output: Hello, John Doe. You are 30 years old.

この引数に分割代入を応用すると、名前付き引数のような感じになりました。

function greet({firstName, lastName, age}) {
    return `Hello, ${firstName} ${lastName}. You are ${age} years old.`;
}

// オブジェクトを使用して引数を渡す
console.log(greet({age: 30, lastName: "Doe", firstName: "John"})); 
// Output: Hello, John Doe. You are 30 years old.

TypeScriptで型をつける

JavaScriptのオブジェクトを使用した「名前付き引数」風の方法を、TypeScriptでより型安全に実装したいと思います。

1. 基本の型付け

オブジェクトの各プロパティに型をつける基本的な方法です。

type GreetParams = {
  firstName: string;
  lastName: string;
  age: number;
}

function greet({ firstName, lastName, age }: GreetParams) {
    return `Hello, ${firstName} ${lastName}. You are ${age} years old.`;
}

console.log(greet({ age: 30, lastName: "Doe", firstName: "John" })); 

2. オプショナルプロパティ

すべてのプロパティが必須でない場合、一部をオプションとして宣言できます。

type GreetParams = {
  firstName?: string;
  lastName?: string;
  age: number;
};

function greet({ firstName = 'John', lastName = 'Doe', age }: GreetParams) {
    return `Hello, ${firstName} ${lastName}. You are ${age} years old.`;
}

console.log(greet({ age: 30 }));
// Output: Hello, John Doe. You are 30 years old.

3. すべてのプロパティをオプションに

すべてのプロパティがオプションの場合、Partialユーティリティタイプを使用します。

function greet({ firstName = 'John', lastName = 'Doe', age = 30 }: Partial<GreetParams>) {
    return `Hello, ${firstName} ${lastName}. You are ${age} years old.`;
}

console.log(greet({})); 

関数に何も引数を渡さなかった場合でも、デフォルトの値を使用して関数を実行できるように、デフォルト引数として空のオブジェクト{}を指定できます。これは、特に多くのオプショナルなパラメータが存在する場合や、関数呼び出し時に何も引数を指定しないことが多い場面で役立ちます。

function greet({ firstName = 'John', lastName = 'Doe', age = 30 }: Partial<GreetParams> = {}) {
    return `Hello, ${firstName} ${lastName}. You are ${age} years old.`;
}


console.log(greet()); 
// Output: Hello, John Doe. You are 30 years old.

この方法で、引数が提供されていない場合や、一部の引数のみが提供されている場合でも、デフォルトの値を使用して関数を正常に実行できます。

まとめ

  • 名前付き引数は、位置ではなく名前ベースで引数を渡すことができる機能
  • JavaScriptには名前付き引数の仕様は存在しないが、オブジェクトと分割代入を利用することで類似の振る舞いを再現することができる
  • TypeScriptを使用すれば、このようなオブジェクトの引数に型をつけて、より堅牢なコードを実現することが可能

今回は名前付き引数の利点を最大限に活用し、コードの可読性や保守性を高めるための方法を調査してみました。JavaScriptやTypeScriptを使ったプロジェクトにおいて、これらのテクニックを取り入れてみてはどうでしょうか。