Rumah >hujung hadapan web >tutorial js >Menguasai TypeScript: Memahami Kuasa extends

Menguasai TypeScript: Memahami Kuasa extends

Barbara Streisand
Barbara Streisandasal
2024-09-22 06:20:08742semak imbas

Mastering TypeScript: Understanding the Power of extends

TypeScript の extends キーワードは、一種のスイス アーミー ナイフです。これは、継承、ジェネリック、条件型などの複数のコンテキストで使用されます。 extends を効果的に使用する方法を理解すると、より堅牢で再利用可能でタ​​イプセーフなコードを作成できます。

拡張を使用した継承

extends の主な用途の 1 つは継承であり、既存のものを基にして新しいインターフェイスやクラスを作成できます。

interface User {
  firstName: string;
  lastName: string;
  email: string;
}

interface StaffUser extends User {
  roles: string[];
  department: string;
}

const regularUser: User = {
  firstName: "John",
  lastName: "Doe",
  email: "john@example.com"
};

const staffMember: StaffUser = {
  firstName: "Jane",
  lastName: "Smith",
  email: "jane@company.com",
  roles: ["Manager", "Developer"],
  department: "Engineering"
};

この例では、StaffUser は User を拡張し、そのすべてのプロパティを継承し、新しいプロパティを追加します。これにより、より一般的な型に基づいて、より具体的な型を作成できるようになります。

クラスの継承

extends キーワードはクラスの継承にも使用されます:

class Animal {
  constructor(public name: string) {}

  makeSound(): void {
    console.log("Some generic animal sound");
  }
}

class Dog extends Animal {
  constructor(name: string, public breed: string) {
    super(name);
  }

  makeSound(): void {
    console.log("Woof! Woof!");
  }

  fetch(): void {
    console.log(`${this.name} is fetching the ball!`);
  }
}

const myDog = new Dog("Buddy", "Golden Retriever");
myDog.makeSound(); // Output: Woof! Woof!
myDog.fetch(); // Output: Buddy is fetching the ball!

ここで、Dog は Animal を拡張し、そのプロパティとメソッドを継承し、独自のプロパティとメソッドも追加しています。

ジェネリックスの型制約

extends キーワードは、ジェネリック関数を使用する場合に非常に重要であり、ジェネリック関数またはクラスで使用できる型を制限できます。

interface Printable {
  print(): void;
}

function printObject<T extends Printable>(obj: T) {
  obj.print();
}

class Book implements Printable {
  print() {
    console.log("Printing a book.");
  }
}

class Magazine implements Printable {
  print() {
    console.log("Printing a magazine.");
  }
}

const myBook = new Book();
const myMagazine = new Magazine();

printObject(myBook);      // Output: Printing a book.
printObject(myMagazine);  // Output: Printing a magazine.
// printObject(42);       // Error, number doesn't have a 'print' method
  1. インターフェイス Printable: ここでは、Printable という名前のインターフェイスを定義します。このインターフェースは、それを実装するクラスが従う必要がある規約を宣言します。この規約では、Printable を実装するクラスは、引数をとらずに void を返す print という名前のメソッドを提供する必要があると指定されています。
  2. function printObject(obj: T): これは printObject という名前の汎用関数です。これは、型 T である obj という名前の単一の引数を受け取ります。型パラメータ T は、この関数の引数として使用できる Printable インターフェイスを拡張 (実装) する型に制限されます。
  3. クラス Book は Printable を実装し、クラス Magazine は Printable を実装します。 ここでは、Book と Magazine という 2 つのクラスを定義します。どちらも Printable インターフェイスを実装します。これは、これらのクラスが、Printable インターフェイスのコントラクトの要求に応じて print メソッドを提供する必要があることを意味します。
  4. const myBook = new Book(); const myMagazine = new Magazine();: Book クラスと Magazine クラスのインスタンスを作成します。
  5. printObject(myBook); and printObject(myMagazine);: Book と Magazine のインスタンスを使用して printObject 関数を呼び出します。 Book クラスと Magazine クラスはどちらも Printable インターフェイスを実装しているため、T extends Printable 型パラメーターの制約を満たします。関数内で、適切なクラスの print メソッドが呼び出され、期待どおりの出力が得られます。
  6. // printObject(42);: Printable インターフェイスを実装していない型 (数値 42 など) で printObject を呼び出そうとすると、TypeScript でエラーが発生します。これは、number に Printable インターフェイスで必要な print メソッドがないため、型制約が満たされていないためです。

要約すると、関数 printObject(obj: T) のコンテキストで extends キーワードを使用して、引数として使用される型 T が Printable インターフェイスによって定義された規約に準拠していることを確認します。これにより、print メソッドを持つ型のみが printObject 関数で使用できるようになり、関数の使用法に特定の動作と規約が適用されます。

条件付きタイプ

T extends U ? X : Y
  • Tはチェックされるタイプです
  • U は、T がチェックされる条件タイプです。
  • X は、T が U を拡張する (割り当て可能である) 場合に条件型が評価する型です。
  • Y は、T が U を拡張しない場合に条件型が評価する型です。
type ExtractNumber<T> = T extends number ? T : never;

type NumberOrNever = ExtractNumber<number>; // number
type StringOrNever = ExtractNumber<string>; // never

ここで、ExtractNumber 型は型パラメーター T を受け取ります。条件付き型は、T が数値型を拡張するかどうかをチェックします。存在する場合、型は T (数値型) に解決されます。そうでない場合、型は none に解決されます。

ユニオン型を使用した extends キーワード

ここで、式 A | を考えてみましょう。 B | C は A を拡張します。これは最初は直観に反しているように思えるかもしれませんが、TypeScript では、この条件は実際には false です。その理由は次のとおりです:

  1. TypeScript では、左側の共用体型で extends を使用する場合、次のように尋ねることと同じです: 「この共用体のすべての可能な型は右側の型に割り当て可能ですか?」
  2. つまり、A | B | C は A の質問を拡張します: 「A を A に割り当てることができ、かつ B を A に割り当てることができ、そして C を A に割り当てることができますか?」
  3. A は確かに A に代入できますが、B と C は (A のサブタイプでない限り) A に代入できない可能性があるため、全体的な結果は false になります。
type Fruit = "apple" | "banana" | "cherry";
type CitrusFruit = "lemon" | "orange";

type IsCitrus<T> = T extends CitrusFruit ? true : false;

type Test1 = IsCitrus<"lemon">; // true
type Test2 = IsCitrus<"apple">; // false
type Test3 = IsCitrus<Fruit>; // false

この例では、IsCitrus Fruit Union のすべてのフルーツが CitrusFruit であるわけではないため、これは false です。

Amalan dan Petua Terbaik

  • Gunakan lanjutan untuk perhubungan yang bermakna: Hanya gunakan warisan apabila terdapat hubungan "is-a" yang jelas antara jenis.
  • Lebih suka gubahan daripada warisan: Dalam kebanyakan kes, gubahan (menggunakan antara muka dan persimpangan jenis) boleh menjadi lebih fleksibel daripada warisan kelas.
  • Berhati-hati dengan rantaian warisan yang mendalam: Warisan mendalam boleh menjadikan kod lebih sukar untuk difahami dan dikekalkan.
  • Manfaatkan jenis bersyarat untuk API fleksibel: Gunakan jenis bersyarat dengan lanjutan untuk mencipta API yang menyesuaikan berdasarkan jenis input.
  • Gunakan lanjutan dalam generik untuk mencipta fungsi boleh guna semula, selamat jenis: Ini membolehkan anda menulis fungsi yang berfungsi dengan pelbagai jenis sambil mengekalkan keselamatan jenis

Atas ialah kandungan terperinci Menguasai TypeScript: Memahami Kuasa extends. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn