課題5: クラス (Class) (所要時間目安: 20‐30分) - hideki5123/myts GitHub Wiki

いよいよクラスの登場です!C#エンジニアの方にとっては最も馴染み深い概念の一つだと思います。TypeScriptのクラスはC#と非常に似ていますが、アクセス修飾子の扱いや命名規則、便利なショートハンド構文など、いくつか重要な違いもあります。

C#との比較:

  • クラス定義 (class Name { ... }):
    • TypeScript & C#: 構文は非常によく似ています。
  • コンストラクタ (constructor):
    • TypeScript: constructor という名前の特別なメソッドで定義します。
    • C#: クラス名と同じ名前のメソッドで定義します。
  • プロパティ:
    • TypeScript: クラス内に直接宣言したり、後述するコンストラクタのパラメータプロパティ(ショートハンド)で定義できます。
    • C#: 通常、フィールドまたはプロパティ ({ get; set; }) として定義します。
  • メソッド:
    • TypeScript & C#: 定義方法は似ています。
  • アクセス修飾子 (public, private, protected):
    • TypeScript & C#: 同じキーワードが存在します。
    • 重要な違い: TypeScriptの private, protected は、主にコンパイル時のチェックです。コンパイル後のJavaScriptコードでは、これらの制限は(基本的には)強制されません (ECMAScriptのプライベートフィールド # を除く)。C#では、アクセス修飾子はコンパイル時およびランタイムで強制されます。
    • TypeScriptでは、修飾子を省略した場合のデフォルトは public です。
  • 命名規則 (Naming Conventions):
    • TypeScript: JavaScriptコミュニティの慣習に従い、メソッド名プロパティ名はアクセス修飾子に関わらず camelCase (例: getUserData, userName) を使うのが一般的です。クラス名、インターフェース名、型エイリアス名、Enum名などの「型」を表すものには PascalCase (例: UserData, IUser) を使います。
    • C#: public なメソッド名やプロパティ名には PascalCase (例: GetUserData, UserName) を使うのが一般的です。
  • コンストラクタのパラメータプロパティ (Shorthand):
    • TypeScript: コンストラクタの引数にアクセス修飾子(public, private など)を付けるだけで、同名のプロパティ宣言とその初期化を自動的に行ってくれる便利なショートハンド構文があります。これはC#にはない特徴です。
  • 継承 (extends):
    • TypeScript: extends キーワードを使います。
    • C#: : 演算子を使います。概念は同じです。

課題内容:

  1. 新しいファイルの作成: src ディレクトリに classes.ts という新しいファイルを作成してください。

  2. クラスの定義と使用: classes.ts に以下のようなクラスを定義し、利用してみてください。(命名規則にも注目してください)

    // src/classes.ts
    
    class Person {
      // プロパティ (public は省略可能だが、ここでは明示)
      public name: string;
      // private プロパティ
      private age: number;
      // protected プロパティ
      protected gender: string;
    
      // コンストラクタ
      constructor(name: string, age: number, gender: string = "unknown") {
        this.name = name;
        this.age = age;
        this.gender = gender;
      }
    
      // public メソッド (camelCase)
      greet(): void {
        console.log(`Hello, my name is ${this.name}. I am ${this.getAgeForGreeting()} years old.`);
      }
    
      // private メソッド (camelCase)
      private getAgeForGreeting(): number {
          return this.age;
      }
    
      // protected メソッド (camelCase)
      protected getGender(): string {
          return this.gender;
      }
    }
    
    const person1 = new Person("Hideki", 30, "male");
    person1.greet();
    console.log(person1.name); // OK
    // console.log(person1.age); // Error: private
    // console.log(person1.gender); // Error: protected
    
    // --- コンストラクタのパラメータプロパティ (Shorthand) ---
    class Animal {
      constructor(public name: string, private species: string, protected sound: string) {}
    
      // public メソッド (camelCase)
      makeSound(): void {
        console.log(`${this.name} (${this.species}) says ${this.sound}`);
      }
    }
    
    const animal1 = new Animal("Leo", "Lion", "Roar");
    animal1.makeSound();
    console.log(animal1.name); // OK
    
    // --- 継承 ---
    class Dog extends Animal {
        constructor(name: string) {
            super(name, "Dog", "Woof");
        }
    
        // メソッドのオーバーライド (camelCase)
        makeSound(): void {
            console.log("Dog barks:");
            super.makeSound();
        }
    
        // public メソッド (camelCase)
        getAnimalSound(): string {
            // サブクラス内からは protected の sound にアクセス可能
            return this.sound;
        }
    }
    
    const dog1 = new Dog("Buddy");
    dog1.makeSound();
    console.log(`Dog's sound (from protected): ${dog1.getAnimalSound()}`);
    
    // ファイル末尾
    export {};
    
  3. コンパイルと実行:

    • npx tsc でコンパイルし、node dist/classes.js で実行して結果を確認してください。
    • コメントアウトされたエラー行を解除して、コンパイル時にアクセス修飾子のチェックが働くことを確認してください。
  4. コミットとPush:

    • src/classes.ts の変更をコミットし (例: git commit -m "Add class examples with naming conventions" )、GitHubにPushしてください。
    • 新しいコミットへのリンクを教えてください。