TypeScript: Interfejsy a typy

713

Jaka jest różnica między tymi instrukcjami (interfejs vs typ)?

interface X {
    a: number
    b: string
}

type X = {
    a: number
    b: string
};
wonea
źródło
4
Znaleziono ten artykuł wyjaśniający różnice - medium.com/@martin_hotell/...
Sandeep GB
1
Podobna różnica
Michael Freidgeim

Odpowiedzi:

572

Zgodnie ze specyfikacją języka TypeScript :

W przeciwieństwie do deklaracji interfejsu, która zawsze wprowadza nazwany typ obiektu, deklaracja aliasu typu może wprowadzić nazwę dla dowolnego rodzaju typu, w tym typów pierwotnych, łączących i przecinających się.

Specyfikacja wspomina dalej:

Typy interfejsów mają wiele podobieństw do aliasów typów dla literałów typu obiektowego, ale ponieważ typy interfejsów oferują więcej możliwości, zazwyczaj preferowane są aliasy typów. Na przykład typ interfejsu

interface Point {
    x: number;
    y: number;
}

można zapisać jako alias typu

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

Takie postępowanie oznacza jednak utratę następujących możliwości:

  • Interfejs może być nazwany w klauzuli extends lub implements, ale alias typu dla literału typu obiektu nie może już być prawdziwy od TS 2.7.
  • Interfejs może mieć wiele scalonych deklaracji , ale alias typu dla literału typu obiektu nie może.
Binary Birch Tree
źródło
109
Co oznacza „wielokrotne scalone deklaracje” w drugiej różnicy?
jrahhali
66
@jrahhali, jeśli zdefiniujesz interfejs dwukrotnie, maszynopis scali je w jeden.
Andriej Fiodorow
39
@jrahhali, jeśli zdefiniujesz typ dwa razy, maszynopis spowoduje błąd
Andrey Fiodorow
18
@jrahhaliinterface Point { x: number; } interface Point { y: number; }
Nahuel Greco
20
Uważam, że pierwsza kwestia extends or implementsjuż nie ma miejsca. Typ można rozszerzyć i zaimplementować za pomocą class. Oto przykład typescriptlang.org/play/...
dark_ruby
774

Aktualizacja 2019


Obecne odpowiedzi i oficjalna dokumentacja są nieaktualne. A dla tych, którzy nie znają TypeScript, zastosowana terminologia nie jest jasna bez przykładów. Poniżej znajduje się lista aktualnych różnic.

1. Obiekty / funkcje

Oba mogą być użyte do opisania kształtu obiektu lub sygnatury funkcji. Ale składnia jest różna.

Berło

interface Point {
  x: number;
  y: number;
}

interface SetPoint {
  (x: number, y: number): void;
}

Wpisz alias

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

type SetPoint = (x: number, y: number) => void;

2. Inne typy

W przeciwieństwie do interfejsu, alias typu może być również używany dla innych typów, takich jak operacje podstawowe, związki i krotki.

// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];

3. Przedłuż

Oba można rozszerzyć, ale znowu składnia jest różna. Dodatkowo zauważ, że interfejs i alias typu nie wykluczają się wzajemnie. Interfejs może rozszerzyć alias typu i odwrotnie.

Interfejs rozszerza interfejs

interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }

Alias ​​typu rozszerza alias typu

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

Interfejs rozszerza alias typu

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }

Alias ​​typu rozszerza interfejs

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

4. Implementuje

Klasa może implementować interfejs lub alias typu, oba w ten sam dokładny sposób. Należy jednak pamiętać, że klasa i interfejs są uważane za statyczne plany. Dlatego nie mogą implementować / rozszerzać aliasu typu, który nazywa typ unii.

interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x = 1;
  y = 2;
}

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

class SomePoint2 implements Point2 {
  x = 1;
  y = 2;
}

type PartialPoint = { x: number; } | { y: number; };

// FIXME: can not implement a union type
class SomePartialPoint implements PartialPoint {
  x = 1;
  y = 2;
}

5. Połączenie deklaracji

W przeciwieństwie do aliasu typu interfejs można zdefiniować wiele razy i będzie on traktowany jako pojedynczy interfejs (z scalonymi elementami wszystkich deklaracji).

// These two declarations become:
// interface Point { x: number; y: number; }
interface Point { x: number; }
interface Point { y: number; }

const point: Point = { x: 1, y: 2 };
jabacchetta
źródło
9
Jeśli oficjalna dokumentacja jest nieaktualna, gdzie można potwierdzić dostarczone informacje?
iX3
59
Na podstawie tego postu wydaje się, że jedynym powodem wyboru interfejsu zamiast aliasu typu jest skorzystanie z funkcji łączenia deklaracji (punkt 5). Poza tym są one równoważne (i argumentowałbym, że aliasy typu oferują bardziej zwięzłą składnię).
maxedison
17
Zawsze używam interfejsów do literału typu obiektu, w przeciwnym razie używanie typów ma większy sens, również myślę, że scalanie deklaracji nie powinno być w żadnym wypadku używane, w rzeczywistości nigdy nie oczekuję, że interfejs zostanie zadeklarowany w innym pliku projektu z niektórymi dodatkowe właściwości, sprawdzanie typu jest pierwotnie wykonane, aby ułatwić ci życie, nie utrudniając korzystania z interfejsów podobnych do ninja: D
Ahmed Kamal
8
Czyli jest to „osobisty” wybór tego, co naprawdę sprawia nam przyjemność? Oprócz jednego powodu możesz po prostu użyć typelub interface? Nadal jestem zdezorientowany, kiedy powinienem użyć jednego lub drugiego.
Joseph Briggs,
7
Czy ktoś mógłby podać motywację, dlaczego chcesz scalić interfejs? Wydaje mi się to potencjalnie mylące. Dlaczego chcesz rozszerzyć definicję interfejsu na różne bloki?
Vanquish46,
95

Począwszy od TypeScript 3.2 (listopad 2018), spełnione są następujące warunki:

wprowadź opis zdjęcia tutaj

Karol Majewski
źródło
9
Czy możesz podać więcej informacji o tym, jak wygenerowano podany przez Ciebie stół / obraz? np. kod źródłowy lub linki do dokumentacji
iX3
23
tak, miałem na myśli źródło treści, a nie jej prezentację.
iX3
3
Nie sądzę, aby klasa mogła rozszerzyć zarówno typ, jak i interfejs, i tak naprawdę nie rozumiem, dlaczego chcesz?
Dan King
7
Unikaj publikowania obrazów tekstu, zamiast tego dołączaj tekst bezpośrednio do posta. Obrazy tekstu nie są łatwe do parsowania ani wyszukiwania i nie są dostępne dla użytkowników niedowidzących.
Andrew Marshall,
2
Ta tabela nie ma żadnych źródeł, które mogłyby obsługiwać jej zawartość i nie polegałbym na niej. Na przykład można zdefiniować typy rekurencyjne przy użyciu typepewnych ograniczeń (i od TypeScript 3.7 te ograniczenia również zniknęły). Interfejsy mogą rozszerzać typy. Klasy mogą implementować typy. Co więcej, przedstawienie danych jako zrzut ekranu tabeli powoduje, że są one całkowicie niedostępne dla osób z zaburzeniami widzenia.
Michał Miszczyszyn,
5

Przykłady z typami:

// utwórz strukturę drzewa dla obiektu. Nie możesz zrobić tego samego z interfejsem z powodu braku skrzyżowania (&)

type Tree<T> = T & { parent: Tree<T> };

// wpisz, aby ograniczyć zmienną do przypisania tylko kilku wartości. Interfejsy nie mają związku (|)

type Choise = "A" | "B" | "C";

// dzięki typom możesz zadeklarować typ NonNullable dzięki mechanizmowi warunkowemu.

type NonNullable<T> = T extends null | undefined ? never : T;

Przykłady z interfejsem:

// możesz użyć interfejsu dla OOP i użyć „implements” do zdefiniowania szkieletu obiektu / klasy

interface IUser {
    user: string;
    password: string;
    login: (user: string, password: string) => boolean;
}

class User implements IUser {
    user = "user1"
    password = "password1"

    login(user: string, password: string) {
        return (user == user && password == password)
    }
}

// możesz rozszerzyć interfejsy o inne interfejsy

    interface IMyObject {
        label: string,
    }

    interface IMyObjectWithSize extends IMyObject{
        size?: number
    }
Przemek Struciński
źródło
-2

dokumentacja wyjaśniła

  • Jedną różnicą jest to, że interfejsy tworzą nową nazwę, która jest używana wszędzie. Aliasy typów nie tworzą nowej nazwy - na przykład komunikaty o błędach nie będą używać nazwy aliasu. W starszych wersjach TypeScript aliasy typów nie mogą być rozszerzane ani implementowane (ani nie mogą rozszerzać / implementować innych typów). Począwszy od wersji 2.7, aliasy typów można rozszerzyć, tworząc nowy typ skrzyżowania
  • Z drugiej strony, jeśli nie możesz wyrazić jakiegoś kształtu za pomocą interfejsu i musisz użyć typu unii lub krotki, aliasy typu są zazwyczaj dobrym rozwiązaniem.

Interfejsy a aliasy typu

Liu Lei
źródło