【TypeScript】オブジェクトを関数の引数に渡す方法

JavaScriptでは、関数に引数を渡す際、オブジェクトを引数として渡すことができます。しかし、JavaScriptだけでは、そのオブジェクトがどんな形や情報を持っているべきかを定義できません。

そこで、TypeScriptを使うと、オブジェクトがどんな形を持つべきか、つまりどんな情報を持っているべきかを「インターフェース」等を用いて、明示的に定義することができるようになります。

この記事では以下の内容を分かりやすく説明するように心掛けています。ご参考になれば幸いです。

  • TypeScriptでオブジェクトを関数の引数に渡す方法
    • 基本的な使い方
    • オブジェクトを直接渡す方法
    • オブジェクトを直接渡して、新たなオブジェクトを作成する方法
      • プロパティ省略記法を用いない場合
      • プロパティ省略記法を用いた場合
    • 分割代入を利用したオブジェクトの各プロパティの参照方法
    • 「オブジェクト」や「配列」が入れ子構造になっているオブジェクトを関数の引数に渡す場合
      • 分割代入を用いない場合
      • 分割代入を用いた場合
    • デフォルト引数を用いて、デフォルト値を設定する方法

TypeScriptでオブジェクトを関数の引数に渡す基本的な使い方

まず、基本的な使い方を説明します。以下のプログラムでは、オブジェクトを引数として関数に渡しています。

// オブジェクトの形状を定義
interface Person {
    name: string;
    age: number;
}

let personInfo: Person = {
    name: "Taro",
    age: 30
};

function displayPersonInfo(person: Person): void {
    console.log("Name: " + person.name);
    console.log("Age: " + person.age);
}

displayPersonInfo(personInfo);

// ログ
// Name: Taro
// Age: 30

上記のプログラムでは、Personというインターフェースを定義し、それをdisplayPersonInfo関数の引数の型として利用しています。

これにより、displayPersonInfo関数に渡されるオブジェクトがPersonインターフェースに準拠していることを保証され、間違った形状のオブジェクトをdisplayPersonInfo関数に渡すことを防ぐことができます。

また、Personインターフェースに準拠しているpersonInfoオブジェクトをdisplayPersonInfo関数の引数に渡しています。

personInfoオブジェクトはnameプロパティとageプロパティを持っており、displayPersonInfo関数の内部でconsole.log関数を用いて、各プロパティの値を参照しています。

オブジェクトを関数の引数に直接渡す方法

以下のプログラムに示すように、関数の呼び出し時に、直接オブジェクトを渡すこともできます。

// オブジェクトの形状を定義
interface Person {
    name: string;
    age: number;
}

function displayPersonInfo(person: Person) {
    console.log("Name: " + person.name);
    console.log("Age: " + person.age);
}

displayPersonInfo({ name: "Taro", age: 30 }); //直接オブジェクトを渡している

// ログ
// Name: Taro
// Age: 30

上記のプログラムでは、{ name: "Taro", age: 30 }displayPersonInfo関数の引数に直接渡しています。

displayPersonInfo関数は、Personインターフェイスに準拠したオブジェクトを引数として受け付けます。なお、Personインターフェイスはstring型のnameとnumber型のageという2つのプロパティを持つことを規定しています。

この方法は、関数が引数として受け取るオブジェクトの形状(プロパティの名前やデータ型)が明確で、その関数を呼び出して、新たなオブジェクトを作成する場合に便利です。次に、新たなオブジェクトを作成しているプログラム例を説明します。

オブジェクトを直接渡して、新たなオブジェクトを作成する方法

以下のプログラムでは、関数を呼び出して、新たなオブジェクトを作成しています。

// オブジェクトの形状を定義
interface Person {
    name: string;
    age: number;
}
interface Payload {
    payload: Person;
}

function wrapInPayload(person: Person): Payload {
    return { payload: person }; //新たなオブジェクトを作成している
}

const result: Payload = wrapInPayload({ name: "Taro", age: 30 });
console.log(result);

// ログ
// { payload: { name: 'Taro', age: 30 } }

このプログラムでは、wrapInPayload関数はPerson型のオブジェクトを引数に取り、そのオブジェクトをpayloadプロパティの値として持つ新しいオブジェクトを作成しています。この新しいオブジェクトはPayload型であり、関数の戻り値の型として指定されています。

上記のプログラムでは、wrapInPayload関数に{ name: "Taro", age: 30 }というオブジェクトを渡しています。この関数は、引数として渡されたオブジェクトを元に、新しいPayload型のオブジェクト{ payload: { name: 'Taro', age: 30 } }を作成し、返します。その返り値は定数resultに格納されています。

プロパティ省略記法について

JavaScript(およびTypeScript)では、「オブジェクトのプロパティ名」と「その値の変数名」が同じ場合、そのプロパティの値を省略することができます。これはプロパティ省略記法(Property Shorthand)といいます。

以下のプログラムでは、関数の引数名がpayloadとなっており、その引数名payloadを関数内部で、payloadプロパティの値にして返しています。

// オブジェクトの形状を定義
interface Person {
    name: string;
    age: number;
}

interface Payload {
    payload: Person;
}

function wrapInPayload(payload: Person): Payload {
    return { payload }; // 「関数の引数名(payload)」と「プロパティの値(payload)」が同じの場合、このように記述できる
}

const result: Payload = wrapInPayload({ name: "Taro", age: 30 });
console.log(result);

// ログ
// { payload: { name: 'Taro', age: 30 } }

このように、プロパティ省略記法は、「関数の引数名(payload)」と「プロパティの値(payload)」が一致するときに有効で、そのような場合にコードをより簡潔に書くことができます。つまり、以下の2つのコードは同じ動作をします。

// 省略記法を用いない場合
function wrapInPayload(payload: Person): Payload {
    return { payload: payload };
}

// 省略記法を用いた場合
function wrapInPayload(payload: Person): Payload {
    return { payload }; 
}

プロパティ省略記法を用いることで、コードをより簡潔に書くことができます。ただし、これは可読性に影響を及ぼすこともあるので、適切な場面で使うことが重要です。

「プロパティ省略記法(Property Shorthand)」は「オブジェクトリテラルの省略記法」、「オブジェクトリテラルプロパティ値の省略記法」、「オブジェクトリテラルプロパティ名の省略記法」とも呼ばれています。この記法はJavaScript ES6(ECMAScript 2015)以降で導入されました。

分割代入を利用したオブジェクトの各プロパティの参照方法

JavaScript(およびTypeScript)には分割代入(Destructuring assignment)という便利な機能があります。

関数の引数に分割代入を利用すると、渡されたオブジェクトから特定のプロパティを取り出すことができます。

以下のプログラムは分割代入を利用したプログラム例です。

// オブジェクトの形状を定義
interface Person {
    name: string;
    age: number;
}

function displayPersonInfo({ name }: Person): void {
    console.log("Name: " + name);
}

let personInfo: Person = {
    name: "Taro",
    age: 30
};

displayPersonInfo(personInfo);

// ログ
// Name: Taro

上記のプログラムでは、Personインターフェースに準拠しているpersonInfoオブジェクトをdisplayPersonInfo関数の引数に渡しています。

personInfoオブジェクトはnameプロパティとageプロパティを持っています。

displayPersonInfo関数は、Personインターフェイスに準拠したオブジェクトを引数として受け付けます。displayPersonInfo関数では、分割代入を利用しており、関数定義時の引数を{ name }: Personと書くことで、personInfoオブジェクトからnameプロパティのみを直接取り出しています。

このように、分割代入でプロパティ名(上記のプログラムでは、name)を指定すれば、プロパティに対応する値(Taro)が引数nameに格納された状態で関数が実行されます。

なお、nameプロパティもageプロパティも取り出す場合には、以下の様に記述します。

// オブジェクトの形状を定義
interface Person {
    name: string;
    age: number;
}

function displayPersonInfo({ name, age }: Person): void {
    console.log("Name: " + name);
    console.log("Age: " + age);
}

let personInfo: Person = {
    name: "Taro",
    age: 30
};

displayPersonInfo(personInfo);

// ログ
// Name: Taro
// Age: 30

「オブジェクト」や「配列」が入れ子構造になっているオブジェクトを関数の引数に渡す場合

「オブジェクト」や「配列」が入れ子構造になっているオブジェクトも関数の引数に渡すことができます。これにより、より複雑なデータ構造を関数に渡すことが可能になります。

分割代入を用いない場合

interface Pet {
    type: string;
    name: string;
}

interface Person {
    name: { first: string, last: string };
    age: number;
    hobbies: string[];
    pets: Pet[];
}

let personInfo: Person = {
    name: { first: "Taro", last: "Yamada" },
    age: 30,
    hobbies: ["music", "traveling"],
    pets: [{type: "dog", name: "Pochi"}, {type: "cat", name: "Tama"}]
};

function displayPersonInfo(person: Person): void {
    console.log("Name: " + person.name.last + person.name.first);
    console.log("Age: " + person.age);

    console.log("Hobbies:");
    for(let hobby of person.hobbies){
        console.log(" - " + hobby);
    }
    
    console.log("Pets:");
    for(let pet of person.pets){
        console.log(" - " + pet.type + ": " + pet.name);
    }
}

displayPersonInfo(personInfo);

// ログ
// Name: YamadaTaro
// Age: 30
// Hobbies:
//  - music
//  - traveling
// Pets:
//  - dog: Pochi
//  - cat: Tama

分割代入を用いる場合

分割代入を用いると、以下のように記述することができます。

interface Pet {
    type: string;
    name: string;
}

interface Person {
    name: { first: string, last: string };
    age: number;
    hobbies: string[];
    pets: Pet[];
}

let personInfo: Person = {
    name: { first: "Taro", last: "Yamada" },
    age: 30,
    hobbies: ["music", "traveling"],
    pets: [{ type: "dog", name: "Pochi" }, { type: "cat", name: "Tama" }]
};

function displayPersonInfo({ name: { first, last }, age, hobbies, pets }: Person) {
    console.log("Name: " + last + first);
    console.log("Age: " + age);

    console.log("Hobbies:");
    for (let hobby of hobbies) {
        console.log(" - " + hobby);
    }

    console.log("Pets:");
    for (let pet of pets) {
        console.log(" - " + pet.type + ": " + pet.name);
    }
}

displayPersonInfo(personInfo);

// ログ
// Name: YamadaTaro
// Age: 30
// Hobbies:
//  - music
//  - traveling
// Pets:
//  - dog: Pochi
//  - cat: Tama

デフォルト引数を用いて、デフォルト値を設定する方法

JavaScript(およびTypeScript)の関数では、デフォルト引数と呼ばれる機能を用いることができます。

デフォルト引数を設定すると、関数実行時に引数が渡されなかった場合や、一部のプロパティしか持たないオブジェクトが渡された場合、関数はデフォルト値(初期値)を使って正常に実行されます。

// オブジェクトの形状を定義
interface Person {
    name?: string;
    age?: number;
}

function displayPersonInfo({ name = "No name", age = 0 }: Person = {}) {
    console.log("Name: " + name);
    console.log("Age: " + age);
}

// オブジェクトを引数に渡さない場合
displayPersonInfo(); 
// ログ
// Name: No name
// Age: 0

// 一部のプロパティを持つオブジェクトを引数に渡す場合
displayPersonInfo({ name: "Taro"}); 
// ログ
// Name: Taro
// Age: 0

上記のプログラムでは、displayPersonInfo関数はデフォルト値を設定した2つのプロパティ(nameage)を持つオブジェクトを引数として受け取っています。

デフォルト値を設定しない場合、「undefined」となります。

// オブジェクトの形状を定義
interface Person {
    name?: string;
    age?: number;
}

function displayPersonInfo({ name, age }: Person = {}) {
    console.log("Name: " + name);
    console.log("Age: " + age);
}

// オブジェクトを引数に渡さない場合
displayPersonInfo();
// ログ
// Name: No name
// Age: 0

// 一部のプロパティを持つオブジェクトを引数に渡す場合
displayPersonInfo({ name: "Taro" });
// ログ
// Name: Taro
// Age: 0

本記事のまとめ

この記事ではTypeScriptで『オブジェクトを関数の引数に渡す方法』について、以下の内容を説明しました。

  • TypeScriptでオブジェクトを関数の引数に渡す方法
    • 基本的な使い方
    • オブジェクトを直接渡す方法
    • オブジェクトを直接渡して、新たなオブジェクトを作成する方法
      • プロパティ省略記法を用いない場合
      • プロパティ省略記法を用いた場合
    • 分割代入を利用したオブジェクトの各プロパティの参照方法
    • 「オブジェクト」や「配列」が入れ子構造になっているオブジェクトを関数の引数に渡す場合
      • 分割代入を用いない場合
      • 分割代入を用いた場合
    • デフォルト引数を用いて、デフォルト値を設定する方法

お読み頂きありがとうございました。