Maison  >  Article  >  interface Web  >  Maîtriser TypeScript : Comprendre la puissance des extensions

Maîtriser TypeScript : Comprendre la puissance des extensions

Barbara Streisand
Barbara Streisandoriginal
2024-09-22 06:20:08711parcourir

Mastering TypeScript: Understanding the Power of extends

Le mot-clé extends dans TypeScript est une sorte de couteau suisse. Il est utilisé dans plusieurs contextes, notamment l'héritage, les génériques et les types conditionnels. Comprendre comment utiliser efficacement extends peut conduire à un code plus robuste, réutilisable et de type sécurisé.

Héritage à l'aide d'extensions

L'une des principales utilisations des extensions est l'héritage, vous permettant de créer de nouvelles interfaces ou classes qui s'appuient sur celles existantes.

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"
};

Dans cet exemple, StaffUser étend User, héritant de toutes ses propriétés et en ajoutant de nouvelles. Cela nous permet de créer des types plus spécifiques basés sur des types plus généraux.

Héritage de classe

Le mot-clé extends est également utilisé pour l'héritage de classe :

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!

Ici, Dog étend Animal, en héritant de ses propriétés et de ses méthodes, et en ajoutant également les siennes.

Contraintes de type dans les génériques

Le mot-clé extends est crucial lorsque l'on travaille avec des génériques, car il nous permet de limiter les types pouvant être utilisés avec une fonction ou une classe générique.

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. interface Printable : Ici, nous définissons une interface nommée Printable. Cette interface déclare un contrat auquel toute classe l'implémentant doit adhérer. Le contrat spécifie que toute classe implémentant Printable doit fournir une méthode nommée print qui ne prend aucun argument et renvoie void
  2. function printObject(obj: T) : Il s'agit d'une fonction générique nommée printObject. Il prend un seul argument nommé obj, qui est de type T. Le paramètre de type T est limité aux types qui étendent (implémentent) l'interface imprimable et peuvent être utilisés comme argument de cette fonction.
  3. la classe Book implémente Printable et la classe Magazine implémente Printable : ici, nous définissons deux classes, Book et Magazine, qui implémentent toutes deux l'interface Printable. Cela signifie que ces classes doivent fournir une méthode d'impression comme l'exige le contrat de l'interface Printable.
  4. const monLivre = nouveau Livre(); et const myMagazine = new Magazine(); : Nous créons des instances des classes Book et Magazine.
  5. printObject(monLivre); et printObject(myMagazine); : Nous appelons la fonction printObject avec les instances de Book et Magazine. Étant donné que les classes Book et Magazine implémentent l’interface Printable, elles remplissent la contrainte du paramètre de type T extends Printable. À l'intérieur de la fonction, la méthode d'impression de la classe appropriée est appelée, ce qui donne le résultat attendu.
  6. // printObject(42); : Si nous essayons d'appeler printObject avec un type qui n'implémente pas l'interface Printable, comme le nombre 42, TypeScript générera une erreur. En effet, la contrainte de type n'est pas satisfaite, car number n'a pas de méthode d'impression comme l'exige l'interface Printable.

En résumé, le mot-clé extends dans le contexte de la fonction printObject(obj: T) est utilisé pour garantir que le type T utilisé comme argument adhère au contrat défini par l'interface Printable. Cela garantit que seuls les types dotés d'une méthode d'impression peuvent être utilisés avec la fonction printObject, appliquant un comportement et un contrat spécifiques pour l'utilisation de la fonction.

Types conditionnels

T extends U ? X : Y
  • T est le type qui est vérifié
  • U est le type de condition par rapport auquel T est vérifié.
  • X est le type auquel le type conditionnel est évalué si T étend (est attribuable à) U
  • Y est le type auquel le type conditionnel est évalué si T n'étend pas U
type ExtractNumber<T> = T extends number ? T : never;

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

Ici, le type ExtractNumber prend un paramètre de type T. Le type conditionnel vérifie si T étend le type numérique. si tel est le cas, le type est résolu en T (qui est le type numérique). Si ce n'est pas le cas, le type est résolu à jamais.

Le mot-clé extends avec les types d’union

Maintenant, considérons l'expression A | B | C étend A. Cela peut sembler contre-intuitif au début, mais dans TypeScript, cette condition est en réalité fausse. Voici pourquoi :

  1. Dans TypeScript, lorsque vous utilisez extends avec un type d'union sur le côté gauche, cela équivaut à demander : "Tous les types possibles dans cette union sont-ils attribuables au type de droite ?"
  2. En d’autres termes, A | B | C étend A demande : "A peut-il être affecté à A, ET B peut-il être affecté à A, ET C peut-il être affecté à A ?"
  3. Bien que A puisse certainement être attribué à A, B et C pourraient ne pas être assignables à A (à moins qu'ils ne soient des sous-types de A), le résultat global est donc faux.
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

Dans cet exemple, IsCitrus est faux car tous les fruits de l'union Fruit ne sont pas des agrumes.

ベストプラクティスとヒント

  • 意味のある関係には extends を使用します: 型間に明確な 「is-a」 関係がある場合にのみ継承を使用します。
  • 継承よりも合成を優先します: 多くの場合、合成 (インターフェイスと型の交差を使用) はクラスの継承よりも柔軟です。
  • 深い継承チェーンには注意してください: 深い継承により、コードの理解と保守が難しくなる可能性があります。
  • 柔軟な API に条件付きタイプを活用する: 条件付きタイプを extends とともに使用して、入力タイプに基づいて適応する API を作成します。
  • ジェネリックで extends を使用して、再利用可能な型安全な関数を作成します: これにより、型安全性を維持しながら、さまざまな型で動作する関数を作成できます

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn