Polski

Kompleksowy przewodnik po interfejsach i typach w TypeScript, omawiający ich różnice, zastosowania i najlepsze praktyki tworzenia skalowalnych aplikacji na całym świecie.

Interfejs a Typ w TypeScript: Najlepsze praktyki deklaracji dla globalnych deweloperów

TypeScript, nadzbiór JavaScriptu, umożliwia programistom na całym świecie tworzenie solidnych i skalowalnych aplikacji dzięki statycznemu typowaniu. Dwie podstawowe konstrukcje do definiowania typów to Interfejsy (Interfaces) i Typy (Types). Chociaż mają wiele podobieństw, zrozumienie ich niuansów i odpowiednich przypadków użycia jest kluczowe do pisania czystego, łatwego w utrzymaniu i wydajnego kodu. Ten kompleksowy przewodnik zagłębi się w różnice między interfejsami a typami w TypeScript, badając najlepsze praktyki ich efektywnego wykorzystania w Twoich projektach.

Zrozumienie interfejsów w TypeScript

Interfejs w TypeScript to potężny sposób na zdefiniowanie kontraktu dla obiektu. Określa on kształt obiektu, precyzując właściwości, które musi posiadać, ich typy danych oraz opcjonalnie, metody, które powinien implementować. Interfejsy głównie opisują strukturę obiektów.

Składnia i przykład interfejsu

Składnia definiowania interfejsu jest prosta:


interface User {
  id: number;
  name: string;
  email: string;
  isActive: boolean;
}

const user: User = {
  id: 123,
  name: "Alice Smith",
  email: "alice.smith@example.com",
  isActive: true,
};

W tym przykładzie interfejs User definiuje strukturę obiektu użytkownika. Każdy obiekt przypisany do zmiennej user musi być zgodny z tą strukturą; w przeciwnym razie kompilator TypeScript zgłosi błąd.

Kluczowe cechy interfejsów

Przykład łączenia deklaracji


interface Window {
  title: string;
}

interface Window {
  height: number;
  width: number;
}

const myWindow: Window = {
  title: "My Application",
  height: 800,
  width: 600,
};

W tym przypadku interfejs Window jest zadeklarowany dwukrotnie. TypeScript łączy te deklaracje, efektywnie tworząc interfejs z właściwościami title, height i width.

Odkrywanie typów w TypeScript

Typ w TypeScript zapewnia sposób na zdefiniowanie kształtu danych. W przeciwieństwie do interfejsów, typy są bardziej wszechstronne i mogą reprezentować szerszy zakres struktur danych, w tym typy prymitywne, unie, intersekcje i krotki.

Składnia i przykład typu

Składnia definiowania aliasu typu jest następująca:


type Point = {
  x: number;
  y: number;
};

const origin: Point = {
  x: 0,
  y: 0,
};

W tym przykładzie typ Point definiuje strukturę obiektu punktu ze współrzędnymi x i y.

Kluczowe cechy typów

Przykład typu unijnego


type Result = {
  success: true;
  data: any;
} | {
  success: false;
  error: string;
};

const successResult: Result = {
  success: true,
  data: { message: "Operation successful!" },
};

const errorResult: Result = {
  success: false,
  error: "An error occurred.",
};

Typ Result jest typem unijnym, który może reprezentować albo sukces z danymi, albo porażkę z komunikatem o błędzie. Jest to przydatne do przedstawiania wyniku operacji, które mogą się powieść lub zakończyć niepowodzeniem.

Przykład typu intersekcyjnego


type Person = {
  name: string;
  age: number;
};

type Employee = {
  employeeId: string;
  department: string;
};

type EmployeePerson = Person & Employee;

const employee: EmployeePerson = {
  name: "Bob Johnson",
  age: 35,
  employeeId: "EMP123",
  department: "Engineering",
};

Typ EmployeePerson jest typem intersekcyjnym, łączącym właściwości zarówno Person, jak i Employee. Pozwala to na tworzenie nowych typów poprzez łączenie istniejących.

Kluczowe różnice: Interfejs a Typ

Chociaż zarówno interfejsy, jak i typy służą do definiowania struktur danych w TypeScript, istnieją kluczowe różnice, które wpływają na to, kiedy używać jednego zamiast drugiego:

  1. Łączenie deklaracji: Interfejsy wspierają łączenie deklaracji, podczas gdy typy nie. Jeśli potrzebujesz rozszerzyć definicję typu na wiele plików lub modułów, generalnie preferowane są interfejsy.
  2. Typy unijne: Typy mogą reprezentować typy unijne, podczas gdy interfejsy nie mogą bezpośrednio definiować unii. Jeśli potrzebujesz zdefiniować typ, który może być jednym z kilku różnych typów, użyj aliasu typu.
  3. Typy intersekcyjne: Typy mogą tworzyć typy intersekcyjne za pomocą operatora &. Interfejsy mogą rozszerzać inne interfejsy, osiągając podobny efekt, ale typy intersekcyjne oferują większą elastyczność.
  4. Typy prymitywne: Typy mogą bezpośrednio reprezentować typy prymitywne (string, number, boolean), podczas gdy interfejsy są przeznaczone głównie do definiowania kształtów obiektów.
  5. Komunikaty o błędach: Niektórzy programiści uważają, że interfejsy oferują nieco jaśniejsze komunikaty o błędach w porównaniu do typów, szczególnie w przypadku złożonych struktur typów.

Najlepsze praktyki: Wybór między interfejsem a typem

Wybór między interfejsami a typami zależy od konkretnych wymagań projektu i osobistych preferencji. Oto kilka ogólnych wytycznych do rozważenia:

Praktyczne przykłady: Scenariusze aplikacji globalnych

Rozważmy kilka praktycznych przykładów, aby zilustrować, jak interfejsy i typy mogą być używane w aplikacji globalnej:

1. Zarządzanie profilem użytkownika (Internacjonalizacja)

Załóżmy, że tworzysz system zarządzania profilami użytkowników, który obsługuje wiele języków. Możesz użyć interfejsów do zdefiniowania struktury profili użytkowników i typów do reprezentowania różnych kodów językowych:


interface UserProfile {
  id: number;
  name: string;
  email: string;
  preferredLanguage: LanguageCode;
  address: Address;
}

interface Address {
    street: string;
    city: string;
    country: string;
    postalCode: string;
}

type LanguageCode = "en" | "fr" | "es" | "de" | "zh"; // Example language codes

const userProfile: UserProfile = {
  id: 1,
  name: "John Doe",
  email: "john.doe@example.com",
  preferredLanguage: "en",
  address: { street: "123 Main St", city: "Anytown", country: "USA", postalCode: "12345" }
};

W tym przypadku interfejs UserProfile definiuje strukturę profilu użytkownika, w tym jego preferowany język. Typ LanguageCode to typ unijny reprezentujący obsługiwane języki. Interfejs Address definiuje format adresu, zakładając ogólny format globalny.

2. Przeliczanie walut (Globalizacja)

Rozważmy aplikację do przeliczania walut, która musi obsługiwać różne waluty i kursy wymiany. Możesz użyć interfejsów do zdefiniowania struktury obiektów walutowych i typów do reprezentowania kodów walut:


interface Currency {
  code: CurrencyCode;
  name: string;
  symbol: string;
}

interface ExchangeRate {
  baseCurrency: CurrencyCode;
  targetCurrency: CurrencyCode;
  rate: number;
}


type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY" | "CAD"; // Example currency codes

const usd: Currency = {
  code: "USD",
  name: "United States Dollar",
  symbol: "$",
};

const exchangeRate: ExchangeRate = {
  baseCurrency: "USD",
  targetCurrency: "EUR",
  rate: 0.85,
};

Interfejs Currency definiuje strukturę obiektu waluty, w tym jej kod, nazwę i symbol. Typ CurrencyCode to typ unijny reprezentujący obsługiwane kody walut. Interfejs ExchangeRate służy do reprezentowania kursów wymiany między różnymi walutami.

3. Walidacja danych (Format międzynarodowy)

Podczas obsługi danych wejściowych od użytkowników z różnych krajów, ważne jest, aby walidować dane zgodnie z poprawnym formatem międzynarodowym. Na przykład numery telefonów mają różne formaty w zależności od kodu kraju. Do reprezentowania tych wariantów można użyć typów.


type PhoneNumber = {
  countryCode: string;
  number: string;
  isValid: boolean; // Add a boolean to represent valid/invalid data.
};

interface Contact {
   name: string;
   phoneNumber: PhoneNumber;
   email: string;
}


function validatePhoneNumber(phoneNumber: string, countryCode: string): PhoneNumber {
  // Validation logic based on countryCode (e.g., using a library like libphonenumber-js)
  // ... Implementation here to validate number.
  const isValid = true; //placeholder

  return { countryCode, number: phoneNumber, isValid };
}

const contact: Contact = {
    name: "Jane Doe",
    phoneNumber: validatePhoneNumber("555-123-4567", "US"), //example
    email: "jane.doe@email.com",
};


console.log(contact.phoneNumber.isValid); //output validation check.

Podsumowanie: Opanowanie deklaracji w TypeScript

Interfejsy i typy w TypeScript to potężne narzędzia do definiowania struktur danych i podnoszenia jakości kodu. Zrozumienie ich różnic i efektywne wykorzystanie jest niezbędne do budowania solidnych, łatwych w utrzymaniu i skalowalnych aplikacji. Postępując zgodnie z najlepszymi praktykami przedstawionymi w tym przewodniku, możesz podejmować świadome decyzje dotyczące tego, kiedy używać interfejsów, a kiedy typów, ostatecznie usprawniając swój proces programowania w TypeScript i przyczyniając się do sukcesu swoich projektów.

Pamiętaj, że wybór między interfejsami a typami jest często kwestią osobistych preferencji i wymagań projektu. Eksperymentuj z oboma podejściami, aby znaleźć to, co najlepiej sprawdza się dla Ciebie i Twojego zespołu. Wykorzystanie potęgi systemu typów TypeScript bez wątpienia doprowadzi do bardziej niezawodnego i łatwiejszego w utrzymaniu kodu, z korzyścią dla programistów na całym świecie.