TypeScriptでクラスのプロパティとメソッドにデフォルト値を設定する方法

TypeScriptでクラスを使用する際、プロパティやメソッドのデフォルト値を設定することは、コードの安定性や柔軟性を高めるための重要な技術です。デフォルト値を設定することで、クラスを使用する際に必ずしもすべての値を初期化する必要がなく、簡潔なコードを書くことが可能になります。特に、複数のオプションを持つコンストラクタや、柔軟なメソッド呼び出しをサポートするためにはデフォルト値が欠かせません。本記事では、TypeScriptにおけるデフォルト値の設定方法とその活用法について、具体例を交えながら解説していきます。

目次

TypeScriptにおけるデフォルト値の重要性

TypeScriptでデフォルト値を設定することは、コードの保守性や可読性を向上させるだけでなく、バグの防止にも役立ちます。特に大規模なプロジェクトでは、すべての引数やプロパティに値を明示的に設定することが難しくなるため、デフォルト値を設定しておくと、クラスやメソッドの使用がより簡単かつエラーが少ないものになります。

デフォルト値の設定は次のような利点があります。

コードの簡潔化

開発者が意図しない値の設定を防ぎ、コードが読みやすくなります。また、メソッドやクラスの呼び出し時に冗長な引数を減らし、シンプルなコードを実現します。

柔軟性の向上

デフォルト値を設定することで、すべてのプロパティやメソッド引数を必ず指定する必要がなくなり、柔軟な設計が可能です。特に、オプションパラメータや設定可能なプロパティが多いクラスでは、この機能が非常に有用です。

バグの防止

デフォルト値を持たない変数やプロパティは、未定義のまま使用される可能性がありますが、デフォルト値を設定することで、これらのリスクを大幅に減少させることができます。

クラスのプロパティにデフォルト値を設定する方法

TypeScriptでクラスのプロパティにデフォルト値を設定するのは非常に簡単です。クラス定義内でプロパティに対して初期値を割り当てるだけで、自動的にそのプロパティにデフォルト値が設定されます。これは、オブジェクトのインスタンス化時に値を渡さなかった場合でも、そのプロパティが未定義になることを防ぎます。

基本的な構文

以下の例では、Personクラスのプロパティnameageにデフォルト値を設定しています。

class Person {
  name: string = "John Doe";
  age: number = 30;
}

const person1 = new Person();
console.log(person1.name); // "John Doe"
console.log(person1.age);  // 30

このコードでは、Personクラスのインスタンスperson1を作成する際に、nameageに何も指定しなくても、デフォルト値が適用されます。これにより、インスタンスを初期化する際に最低限の設定で済むため、コードがよりシンプルになります。

コンストラクタとデフォルト値の併用

クラスコンストラクタで渡された引数を使用しつつ、デフォルト値を保持することも可能です。コンストラクタ内でプロパティに渡された値が存在しない場合、デフォルト値が利用されます。

class Person {
  name: string;
  age: number;

  constructor(name: string = "John Doe", age: number = 30) {
    this.name = name;
    this.age = age;
  }
}

const person2 = new Person("Alice");
console.log(person2.name); // "Alice"
console.log(person2.age);  // 30

ここでは、nameには引数として”John Doe”以外の値を渡していますが、ageは指定されていないため、デフォルト値の30が適用されます。このように、デフォルト値を設定することで、クラスを柔軟に使用できるようになります。

メソッド引数にデフォルト値を設定する方法

TypeScriptでは、クラス内のメソッドに引数のデフォルト値を設定することも簡単です。デフォルト値を指定すると、メソッドを呼び出す際にその引数が省略された場合、自動的に指定したデフォルト値が使われるようになります。これにより、コードがより柔軟になり、冗長な引数の指定を避けることができます。

基本的な構文

以下の例では、greetというメソッドにgreeting引数を設定し、そのデフォルト値を "Hello" にしています。

class Greeter {
  greet(greeting: string = "Hello"): void {
    console.log(greeting);
  }
}

const greeter = new Greeter();
greeter.greet();           // "Hello"
greeter.greet("Hi there"); // "Hi there"

このコードでは、greetメソッドのgreeting引数に値が渡されない場合、自動的に "Hello" というデフォルト値が使用されます。引数を明示的に渡した場合は、その値が優先されます。

複数引数にデフォルト値を設定する

TypeScriptでは、複数の引数にデフォルト値を設定することができます。ただし、デフォルト値を持つ引数は、その後に続く引数があっても省略することができるため、順序にも注意が必要です。

class Calculator {
  calculate(a: number = 1, b: number = 2, operation: string = "add"): number {
    if (operation === "add") {
      return a + b;
    } else if (operation === "subtract") {
      return a - b;
    }
    return 0;
  }
}

const calculator = new Calculator();
console.log(calculator.calculate());              // 3 (1 + 2)
console.log(calculator.calculate(5));             // 7 (5 + 2)
console.log(calculator.calculate(5, 3, "subtract")); // 2 (5 - 3)

この例では、3つの引数にデフォルト値を設定しています。デフォルト値が適用されるのは、引数が省略された場合のみです。operation"subtract" を指定することで、引き算も可能となっています。

デフォルト値とオプショナル引数の違い

デフォルト値を持つ引数と、TypeScriptでサポートされているオプショナル引数(? で定義する)とは異なります。オプショナル引数は省略可能な引数で、値が渡されなければ undefined になります。一方、デフォルト値を持つ引数は、値が省略された場合にそのデフォルト値が使用されます。

class Example {
  printMessage(message?: string): void {
    console.log(message || "Default message");
  }
}

const example = new Example();
example.printMessage();        // "Default message"
example.printMessage("Hello"); // "Hello"

この例では、オプショナル引数のmessageが省略された場合、undefined ではなく "Default message" を表示するようにしています。このように、デフォルト値とオプショナル引数の使い分けによって、メソッドの設計に柔軟性を持たせることができます。

デフォルト値の使用例:クラスプロパティ編

TypeScriptでクラスプロパティにデフォルト値を設定することで、クラスインスタンスの初期化時にプロパティの初期値を省略しても動作するようにすることができます。これは、複数のインスタンスを作成する際に、異なるプロパティを持つ場合でも、共通のデフォルト設定を保持するのに役立ちます。

例1: ユーザークラスのプロパティにデフォルト値を設定

次に示す例では、UserクラスのnameageisAdminというプロパティにデフォルト値を設定しています。

class User {
  name: string = "Unknown";
  age: number = 18;
  isAdmin: boolean = false;
}

const user1 = new User();
console.log(user1.name);   // "Unknown"
console.log(user1.age);    // 18
console.log(user1.isAdmin); // false

const user2 = new User();
user2.name = "Alice";
console.log(user2.name);   // "Alice"

この例では、user1はデフォルト値を使って初期化されますが、user2ではnameプロパティが上書きされて、 "Alice" となっています。ageisAdminのような他のプロパティはそのままデフォルト値が使われます。

例2: コンストラクタでデフォルト値を上書き可能にする

プロパティにデフォルト値を設定するだけでなく、コンストラクタを使ってそれらの値を変更できる柔軟性を持たせることもできます。以下の例では、Productクラスのプロパティに対して、コンストラクタで任意に値を渡せるようにしています。

class Product {
  name: string;
  price: number = 100;
  inStock: boolean = true;

  constructor(name: string = "Unknown Product", price?: number, inStock?: boolean) {
    this.name = name;
    if (price !== undefined) {
      this.price = price;
    }
    if (inStock !== undefined) {
      this.inStock = inStock;
    }
  }
}

const defaultProduct = new Product();
console.log(defaultProduct.name);   // "Unknown Product"
console.log(defaultProduct.price);  // 100
console.log(defaultProduct.inStock); // true

const customProduct = new Product("Laptop", 1500, false);
console.log(customProduct.name);   // "Laptop"
console.log(customProduct.price);  // 1500
console.log(customProduct.inStock); // false

この例では、nameのデフォルト値として "Unknown Product" を設定していますが、customProductのインスタンス作成時にカスタムの値を渡すことができるため、柔軟にインスタンスを生成できます。

クラスプロパティにデフォルト値を設定するメリット

プロパティにデフォルト値を設定することは以下のような利点をもたらします。

コードの可読性とメンテナンス性の向上

デフォルト値があることで、クラスの利用者はそのプロパティの標準的な動作を理解しやすくなり、コードのメンテナンスが容易になります。さらに、クラス定義内に値が明示されているため、他の開発者もプロパティの目的を直感的に理解できます。

インスタンス化時のエラー防止

すべてのプロパティに必ずしも値を渡す必要がないため、コンストラクタを複雑にすることなくクラスインスタンスを柔軟に生成できます。また、誤って未定義のプロパティにアクセスしてしまうリスクも軽減されます。

このように、クラスのプロパティにデフォルト値を設定することで、コードの安定性を保ちながら柔軟な設計が可能になります。

デフォルト値の使用例:メソッド引数編

TypeScriptでは、メソッドの引数にもデフォルト値を設定することが可能です。これにより、メソッド呼び出し時に引数を省略しても、デフォルトの動作を保証することができます。デフォルト値を設定することで、メソッドの柔軟性が向上し、引数を省略したい場合でも簡単に利用できるようになります。

例1: デフォルト値を使った簡単なメソッド

次の例では、introduceというメソッドに2つの引数namegreetingを持たせています。greetingにはデフォルト値 "Hello" を設定しているため、引数を渡さない場合でも挨拶が表示されます。

class Person {
  introduce(name: string, greeting: string = "Hello"): string {
    return `${greeting}, my name is ${name}.`;
  }
}

const person = new Person();
console.log(person.introduce("Alice"));            // "Hello, my name is Alice."
console.log(person.introduce("Bob", "Hi there"));  // "Hi there, my name is Bob."

この例では、introduceメソッドを呼び出す際にgreetingを省略すると、デフォルト値の "Hello" が使われます。一方、greetingを指定すればその値が使用されます。

例2: 複数の引数にデフォルト値を設定する

複数の引数にデフォルト値を設定する場合、それぞれの引数に対して省略可能なデフォルト値を設定できます。次の例では、addメソッドにデフォルト値を設定し、足し算を行うメソッドを柔軟にしています。

class Calculator {
  add(a: number = 0, b: number = 0): number {
    return a + b;
  }
}

const calculator = new Calculator();
console.log(calculator.add());         // 0 (デフォルト値: 0 + 0)
console.log(calculator.add(5));        // 5 (5 + 0)
console.log(calculator.add(3, 7));     // 10 (3 + 7)

この例では、2つの引数abにデフォルト値を設定しています。どちらの引数も省略可能で、指定しなければ0が使用されます。このように、メソッドに対して柔軟な呼び出し方法を提供することができます。

例3: コンストラクタメソッドでのデフォルト値の利用

コンストラクタにおいても、引数にデフォルト値を設定することで、クラスのインスタンスを作成する際の利便性が高まります。次の例では、Rectangleクラスのコンストラクタにデフォルト値を設定し、オプションで幅と高さを指定できるようにしています。

class Rectangle {
  width: number;
  height: number;

  constructor(width: number = 10, height: number = 5) {
    this.width = width;
    this.height = height;
  }

  area(): number {
    return this.width * this.height;
  }
}

const defaultRectangle = new Rectangle();
console.log(defaultRectangle.area());   // 50 (10 * 5)

const customRectangle = new Rectangle(20, 15);
console.log(customRectangle.area());    // 300 (20 * 15)

この例では、Rectangleクラスのコンストラクタで、widthheightにデフォルト値を設定しています。デフォルトの値が使用される場合、幅10、高さ5の長方形が生成され、指定があればそれに応じた値が適用されます。

メソッド引数にデフォルト値を設定するメリット

メソッド引数にデフォルト値を設定することには、いくつかの重要な利点があります。

柔軟性の向上

引数が多いメソッドでも、デフォルト値を設定することで、呼び出し時にすべての引数を指定する必要がなくなり、柔軟に利用できます。省略した引数には自動的にデフォルト値が使われるため、簡潔なコードが書けます。

エラーハンドリングの簡素化

引数が省略された場合にundefinednullに依存するのではなく、デフォルト値があることで明示的に動作を制御でき、エラーハンドリングの手間を減らすことができます。

このように、メソッド引数にデフォルト値を設定することで、コードの柔軟性や保守性が向上し、より効率的なプログラミングが可能になります。

デフォルト値設定の際の注意点とベストプラクティス

TypeScriptでデフォルト値を設定することは非常に便利ですが、その利用にはいくつかの注意点とベストプラクティスがあります。デフォルト値を効果的に使うことで、コードの保守性や可読性を向上させ、予期しないバグを防ぐことができます。

注意点1: デフォルト値の順序

デフォルト値を持つ引数は、デフォルト値を持たない引数の後に配置する必要があります。これは、TypeScriptが関数呼び出し時に引数の位置に基づいて値を渡すため、デフォルト値を持つ引数が先に来てしまうと、混乱を引き起こす可能性があるからです。

function example(a: number, b: number = 10) {
  return a + b;
}

console.log(example(5));   // 15 (a = 5, b = 10)

この場合、bはデフォルト値を持つため省略可能ですが、デフォルト値を持たない引数aが前に来ているため、関数呼び出し時に問題が発生しません。もし逆の順序にすると、関数呼び出しが曖昧になります。

注意点2: `undefined`でデフォルト値が使われる挙動

デフォルト値は、引数がundefinedとして渡された場合にも適用されます。しかし、nullが渡された場合はデフォルト値は使用されず、明示的にnullが引数の値として設定されます。この挙動を理解しておくことが重要です。

function greet(name: string = "Guest") {
  return `Hello, ${name}`;
}

console.log(greet(undefined)); // "Hello, Guest"
console.log(greet(null));      // "Hello, null"

この例では、undefinedが渡された場合はデフォルト値が使用されますが、nullが渡された場合はそのままnullが適用されます。

ベストプラクティス1: デフォルト値を過度に多用しない

デフォルト値は便利ですが、すべてのプロパティや引数にデフォルト値を設定するのは避けるべきです。あまりに多くのデフォルト値を設定すると、コードが複雑になり、逆に予期しない動作を引き起こす可能性があります。デフォルト値は、開発者が設定しなくても適切な値が推測できる場面に限定して使うのが良いです。

ベストプラクティス2: デフォルト値の使用は明確に

デフォルト値を設定する際には、クラスやメソッドのドキュメントでその存在を明確にすることが重要です。これにより、他の開発者がそのクラスやメソッドを使用する際に混乱せず、想定通りの動作を理解することができます。コメントやTypeScriptの型定義ファイルにデフォルト値を明記しておくと良いでしょう。

ベストプラクティス3: 単純なデフォルト値を利用する

デフォルト値にはできるだけ単純で予測可能な値を使用しましょう。複雑なオブジェクトや関数をデフォルト値に設定すると、予期しない副作用を引き起こすことがあります。単純なリテラル値や基礎的な型を使うことが推奨されます。

class Settings {
  theme: string = "light";
  notificationsEnabled: boolean = true;
}

このように単純なデフォルト値を設定することで、コードの読みやすさと予測可能な動作を維持することができます。

ベストプラクティス4: 複雑なデフォルト値には関数を使う

デフォルト値に計算や動的な処理が必要な場合は、直接値を設定するのではなく、関数を使ってその場で値を生成する方法が有効です。これにより、コードの可読性を保ちながら、柔軟な処理が可能になります。

class Config {
  apiUrl: string;
  timeout: number;

  constructor(apiUrl: string = "https://api.example.com", timeout: number = Config.defaultTimeout()) {
    this.apiUrl = apiUrl;
    this.timeout = timeout;
  }

  static defaultTimeout(): number {
    return new Date().getHours() < 18 ? 3000 : 5000;
  }
}

このように、複雑なロジックをデフォルト値に持たせる場合は、関数を用いることでコードの整理ができます。

ベストプラクティス5: 型安全性を確保する

デフォルト値を設定する際には、型安全性にも注意しましょう。特に、複雑な型を扱う場合、意図しない型の値がデフォルトとして設定されると、ランタイムエラーの原因になる可能性があります。TypeScriptの型チェックを活用して、安全なデフォルト値を設定することが重要です。

このように、デフォルト値を設定する際の注意点とベストプラクティスを理解しておくことで、TypeScriptのコードがより柔軟で堅牢なものになります。

応用例:オプショナルプロパティとの併用

TypeScriptでは、デフォルト値の設定とオプショナルプロパティ(任意プロパティ)を併用することで、柔軟なクラス設計が可能になります。オプショナルプロパティは、値が指定されない場合でもundefinedとなることを許容するため、デフォルト値と組み合わせることでより高度な設定ができます。

オプショナルプロパティとは

オプショナルプロパティは、?記号を使って指定することで、そのプロパティが必須ではないことを表します。これにより、プロパティが存在しない場合でもエラーが発生しません。デフォルト値と異なり、オプショナルプロパティに値が設定されない場合はundefinedになります。

class User {
  name?: string;
  age?: number;
  isAdmin: boolean = false;
}

const user1 = new User();
console.log(user1.name);   // undefined
console.log(user1.isAdmin); // false

この例では、nameageはオプショナルプロパティなので、値を設定しなくてもエラーになりません。一方で、isAdminにはデフォルト値が設定されているため、インスタンス化時に値がない場合はfalseが適用されます。

デフォルト値とオプショナルプロパティの組み合わせ

オプショナルプロパティとデフォルト値を組み合わせることで、デフォルト値が設定されていない場合にも適切に動作する柔軟なクラス設計が可能です。例えば、コンストラクタでオプショナルな引数を指定し、デフォルト値がない場合はundefinedをチェックして、必要に応じて値を設定します。

class Product {
  name: string;
  price: number;
  discount?: number;

  constructor(name: string = "Unknown", price: number = 100, discount?: number) {
    this.name = name;
    this.price = price;
    this.discount = discount ?? 0; // discountがundefinedなら0を設定
  }

  finalPrice(): number {
    return this.price - this.discount!;
  }
}

const product1 = new Product();
console.log(product1.finalPrice()); // 100 (デフォルト値使用、割引なし)

const product2 = new Product("Laptop", 1500, 200);
console.log(product2.finalPrice()); // 1300 (1500 - 200)

この例では、discountはオプショナルプロパティとして定義されています。コンストラクタ内では、discountundefinedであれば、デフォルト値として0を設定しています。これにより、プロパティの有無によって柔軟に動作するクラスを実現しています。

オプショナルプロパティの活用例

次の例では、Settingsクラスでオプショナルプロパティとデフォルト値を組み合わせ、ユーザーが一部の設定だけを指定しても適切に処理されるように設計しています。

class Settings {
  theme: string;
  notificationsEnabled?: boolean;

  constructor(theme: string = "light", notificationsEnabled?: boolean) {
    this.theme = theme;
    this.notificationsEnabled = notificationsEnabled ?? true; // オプショナルプロパティにデフォルト値を設定
  }

  showSettings(): string {
    return `Theme: ${this.theme}, Notifications: ${this.notificationsEnabled ? "Enabled" : "Disabled"}`;
  }
}

const settings1 = new Settings();
console.log(settings1.showSettings()); // "Theme: light, Notifications: Enabled"

const settings2 = new Settings("dark", false);
console.log(settings2.showSettings()); // "Theme: dark, Notifications: Disabled"

この例では、themeにデフォルト値を設定し、notificationsEnabledをオプショナルプロパティとして扱っています。notificationsEnabledundefinedの場合はデフォルトでtrueを設定し、それ以外の場合はユーザー指定の値が反映されます。

オプショナルプロパティとデフォルト値を使うメリット

デフォルト値とオプショナルプロパティの併用は、以下の利点をもたらします。

柔軟なクラス設計

必須のプロパティとオプショナルなプロパティを適切に分けることで、複雑な設定やデータ構造を柔軟に扱うことができます。ユーザーが全てのプロパティを指定しなくても、クラスは適切に動作します。

コードの簡潔化

オプショナルプロパティとデフォルト値を使うことで、複雑な初期化ロジックが不要になり、シンプルでメンテナンスしやすいコードを書くことができます。また、開発者は必要なプロパティだけを指定できるため、コードの記述量を減らせます。

未定義値のハンドリングが容易

オプショナルプロパティに対してundefinedチェックを行いながらデフォルト値を設定することで、未定義の値を適切に処理し、予期せぬエラーの発生を防ぎます。

このように、オプショナルプロパティとデフォルト値を適切に組み合わせることで、TypeScriptでより柔軟かつ堅牢なクラス設計が可能になります。

デフォルト値を持つクラスのテスト方法

TypeScriptでデフォルト値を持つクラスやメソッドを設計する際、正しく動作することを確認するために、適切なテストを行うことが非常に重要です。特にデフォルト値を設定したプロパティや引数に対する挙動が正しいかどうかを検証するためのテストは、コードの信頼性を向上させるための基本です。

このセクションでは、デフォルト値を持つクラスやメソッドをテストする具体的な方法を紹介します。

ユニットテストの重要性

デフォルト値を持つクラスやメソッドに対するユニットテストを行うことで、次のような利点があります。

  1. デフォルト値が意図通りに適用されているかを確認できる。
  2. デフォルト値が上書きされた場合、正しい結果が得られるかを検証できる。
  3. メソッドやクラスの予期しない動作を未然に防ぐことができる。

Jestを使ったテスト例

JavaScriptやTypeScriptでのテストには、一般的にJestなどのテストフレームワークが使用されます。以下の例では、デフォルト値を持つクラスやメソッドに対する基本的なテストケースを紹介します。

// Product.ts
class Product {
  name: string;
  price: number;
  discount?: number;

  constructor(name: string = "Unknown", price: number = 100, discount?: number) {
    this.name = name;
    this.price = price;
    this.discount = discount ?? 0;
  }

  finalPrice(): number {
    return this.price - this.discount!;
  }
}

export default Product;

次に、このProductクラスに対するテストケースを作成します。

// Product.test.ts
import Product from './Product';

describe('Product class tests', () => {
  test('should apply default values when no arguments are provided', () => {
    const product = new Product();
    expect(product.name).toBe('Unknown');
    expect(product.price).toBe(100);
    expect(product.discount).toBe(0);
    expect(product.finalPrice()).toBe(100);
  });

  test('should use provided values instead of default ones', () => {
    const product = new Product('Laptop', 1500, 200);
    expect(product.name).toBe('Laptop');
    expect(product.price).toBe(1500);
    expect(product.discount).toBe(200);
    expect(product.finalPrice()).toBe(1300);
  });

  test('should apply default discount when only name and price are provided', () => {
    const product = new Product('Phone', 800);
    expect(product.name).toBe('Phone');
    expect(product.price).toBe(800);
    expect(product.discount).toBe(0);
    expect(product.finalPrice()).toBe(800);
  });
});

テストケースの詳細

上記のテストコードでは、デフォルト値の挙動を検証するためのいくつかのテストケースを作成しています。

デフォルト値が適用されるかを確認するテスト

最初のテストケースでは、Productクラスに引数を与えずにインスタンス化した場合、デフォルト値が適切に適用されるかを確認しています。この場合、name"Unknown"price100、そしてdiscount0というデフォルト値が設定されることを期待しています。

test('should apply default values when no arguments are provided', () => {
  const product = new Product();
  expect(product.name).toBe('Unknown');
  expect(product.price).toBe(100);
  expect(product.discount).toBe(0);
  expect(product.finalPrice()).toBe(100);
});

提供された引数が正しく適用されるかを確認するテスト

次のテストケースでは、インスタンス作成時にすべての引数を指定した場合、デフォルト値が上書きされ、指定した値が適切に反映されるかを確認しています。

test('should use provided values instead of default ones', () => {
  const product = new Product('Laptop', 1500, 200);
  expect(product.name).toBe('Laptop');
  expect(product.price).toBe(1500);
  expect(product.discount).toBe(200);
  expect(product.finalPrice()).toBe(1300);
});

一部の引数だけを提供した場合のテスト

最後に、一部の引数だけが渡された場合に、未提供の引数にデフォルト値が適用されるかどうかを確認するテストです。ここではdiscountに値が指定されていないため、デフォルト値の0が適用されます。

test('should apply default discount when only name and price are provided', () => {
  const product = new Product('Phone', 800);
  expect(product.name).toBe('Phone');
  expect(product.price).toBe(800);
  expect(product.discount).toBe(0);
  expect(product.finalPrice()).toBe(800);
});

テストのベストプラクティス

デフォルト値を持つクラスやメソッドをテストする際は、以下のポイントを意識しましょう。

1. すべてのパターンをカバーする

デフォルト値が使用される場合と、指定された値が適用される場合の両方をしっかりとテストし、各パターンで正しい動作を確認します。

2. 異常系のテストも行う

無効な引数が渡された場合や引数が不完全な場合にも、エラーが発生せず期待通りに動作するかをテストすることが重要です。

3. 環境依存の動作に注意する

特定の条件に依存するデフォルト値(たとえば日付やランダムな値)は、テストが安定して動作するようにモックやスタブを活用して制御する必要があります。

これらの方法を用いて、デフォルト値を持つクラスやメソッドを効率的にテストすることで、コードの品質を高めることができます。

演習問題:デフォルト値を活用したクラス設計

ここでは、TypeScriptでデフォルト値を活用したクラス設計の演習問題を紹介します。これにより、実際にコードを作成してデフォルト値の設定方法や、オプショナルプロパティとの組み合わせを学びます。

問題1: クラス`Car`の作成

次の仕様に従って、Carクラスを作成してください。

  • プロパティとしてmake(メーカー)、model(モデル)、year(年式)を持つ。
  • makeにはデフォルト値として"Unknown"modelにはデフォルト値として"Generic"yearにはデフォルト値として2000を設定する。
  • メソッドdisplayInfoを実装し、"Car: make, model (year)"という形式で車の情報を表示する。
  • オプションで、年式を指定できるが、指定されない場合はデフォルト値を適用する。

期待する動作例

const car1 = new Car();
console.log(car1.displayInfo()); // "Car: Unknown, Generic (2000)"

const car2 = new Car("Toyota", "Corolla", 2021);
console.log(car2.displayInfo()); // "Car: Toyota, Corolla (2021)"

解答例

class Car {
  make: string;
  model: string;
  year: number;

  constructor(make: string = "Unknown", model: string = "Generic", year: number = 2000) {
    this.make = make;
    this.model = model;
    this.year = year;
  }

  displayInfo(): string {
    return `Car: ${this.make}, ${this.model} (${this.year})`;
  }
}

const car1 = new Car();
console.log(car1.displayInfo()); // "Car: Unknown, Generic (2000)"

const car2 = new Car("Toyota", "Corolla", 2021);
console.log(car2.displayInfo()); // "Car: Toyota, Corolla (2021)"

この例では、Carクラスに対して、すべての引数にデフォルト値が設定されています。makemodelにデフォルト値を使いながら、特定のインスタンスではこれを上書きすることができます。

問題2: クラス`User`の作成

次に、ユーザー情報を管理するUserクラスを作成してください。

  • プロパティとしてusername(ユーザー名)、email(メールアドレス)、isAdmin(管理者フラグ)を持つ。
  • usernameはデフォルトで"guest"emailはデフォルトで空文字列""isAdminfalseとする。
  • メソッドgetUserInfoを実装し、ユーザー名、メールアドレス、および管理者かどうかの情報を返す。
  • isAdmintrueの場合は、"Admin"falseの場合は"User"と表示する。

期待する動作例

const user1 = new User();
console.log(user1.getUserInfo()); // "guest (User), Email: "

const user2 = new User("john_doe", "john@example.com", true);
console.log(user2.getUserInfo()); // "john_doe (Admin), Email: john@example.com"

解答例

class User {
  username: string;
  email: string;
  isAdmin: boolean;

  constructor(username: string = "guest", email: string = "", isAdmin: boolean = false) {
    this.username = username;
    this.email = email;
    this.isAdmin = isAdmin;
  }

  getUserInfo(): string {
    const role = this.isAdmin ? "Admin" : "User";
    return `${this.username} (${role}), Email: ${this.email}`;
  }
}

const user1 = new User();
console.log(user1.getUserInfo()); // "guest (User), Email: "

const user2 = new User("john_doe", "john@example.com", true);
console.log(user2.getUserInfo()); // "john_doe (Admin), Email: john@example.com"

この例では、Userクラスのプロパティにデフォルト値を設定し、ユーザーが一部の情報しか提供しない場合にも問題なく動作するようになっています。また、isAdminのフラグによって役割を表示する柔軟性も持たせています。

問題3: クラス`Order`の作成

最後に、注文情報を管理するOrderクラスを作成してください。

  • プロパティとしてorderId(注文ID)、amount(注文金額)、status(注文状況)を持つ。
  • orderIdは必須のプロパティで、amountはデフォルトで0status"Pending"というデフォルト値を持つ。
  • メソッドgetOrderSummaryを実装し、"Order ID: orderId, Amount: amount, Status: status"という形式で注文情報を表示する。

期待する動作例

const order1 = new Order("12345");
console.log(order1.getOrderSummary()); // "Order ID: 12345, Amount: 0, Status: Pending"

const order2 = new Order("67890", 250, "Shipped");
console.log(order2.getOrderSummary()); // "Order ID: 67890, Amount: 250, Status: Shipped"

解答例

class Order {
  orderId: string;
  amount: number;
  status: string;

  constructor(orderId: string, amount: number = 0, status: string = "Pending") {
    this.orderId = orderId;
    this.amount = amount;
    this.status = status;
  }

  getOrderSummary(): string {
    return `Order ID: ${this.orderId}, Amount: ${this.amount}, Status: ${this.status}`;
  }
}

const order1 = new Order("12345");
console.log(order1.getOrderSummary()); // "Order ID: 12345, Amount: 0, Status: Pending"

const order2 = new Order("67890", 250, "Shipped");
console.log(order2.getOrderSummary()); // "Order ID: 67890, Amount: 250, Status: Shipped"

この問題では、orderIdが必須の引数であり、その他のプロパティにデフォルト値を設定しています。これにより、柔軟かつ簡潔な注文管理が可能になっています。

演習問題のまとめ

これらの演習問題では、TypeScriptでクラスのプロパティやメソッドにデフォルト値を設定する方法を学びました。デフォルト値を使うことで、クラスを柔軟に設計でき、ユーザーにとって使いやすいAPIを提供できます。

まとめ

本記事では、TypeScriptにおけるクラスプロパティやメソッド引数にデフォルト値を設定する方法について解説しました。デフォルト値を設定することで、コードの柔軟性や保守性が向上し、複雑な引数管理をシンプルにすることができます。また、オプショナルプロパティとの組み合わせにより、さらに高度なクラス設計が可能となります。適切にデフォルト値を活用することで、エラーを減らし、予期しない挙動を防ぎ、効率的な開発を実現できるようになります。

コメント

コメントする

目次