Przedstawiliśmy główne cechy systemu typów TypeScript, kiedy omawialiśmy Dlaczego TypeScript? Poniżej znajduje się kilka kluczowych wniosków z tej dyskusji, które nie wymagają dalszych wyjaśnień:
-
System typów w TypeScript został zaprojektowany jako opcjonalny, aby Twój JavaScript był TypeScript.
-
TypeScript nie blokuje emisji JavaScript w obecności błędów typu, pozwalając ci stopniowo aktualizować swój JS do TS.
Teraz zacznijmy od składni systemu typów TypeScript. W ten sposób możesz od razu zacząć używać tych adnotacji w swoim kodzie i zobaczyć korzyści. To przygotuje cię do głębszego nurkowania później.
Jak wspomniano wcześniej, typy są adnotowane przy użyciu składni :TypeAnnotation
. Wszystko, co jest dostępne w przestrzeni deklaracji typu, może być użyte jako adnotacja typu.
Następujący przykład demonstruje adnotacje typu dla zmiennych, parametrów funkcji i wartości zwrotnych funkcji:
var num: number = 123;function identity(num: number): number {return num;}
Typy prymitywne
Typy prymitywne JavaScript są dobrze reprezentowane w systemie typów TypeScript. Oznacza to string
, number
, boolean
jak zademonstrowano poniżej:
var num: number;var str: string;var bool: boolean;num = 123;num = 123.456;num = '123'; // Errorstr = '123';str = 123; // Errorbool = true;bool = false;bool = 'false'; // Error
Tablice
TypeScript zapewnia dedykowaną składnię typów dla tablic, aby ułatwić Ci adnotacje i dokumentowanie Twojego kodu. Składnia ta jest w zasadzie postfiksem do dowolnej poprawnej adnotacji typu (np.
:boolean
). Pozwala ci to bezpiecznie wykonywać dowolne manipulacje tablicowe, które normalnie byś robił i chroni cię przed błędami, takimi jak przypisanie członka niewłaściwego typu. Jest to zademonstrowane poniżej:
var boolArray: boolean;boolArray = ;console.log(boolArray); // trueconsole.log(boolArray.length); // 2boolArray = true;boolArray = ;boolArray = 'false'; // Error!boolArray = 'false'; // Error!boolArray = ; // Error!
Interfejsy
Interfejsy są podstawowym sposobem w TypeScript do kompilacji wielu adnotacji typu w pojedynczą nazwaną adnotację. Weźmy pod uwagę następujący przykład:
interface Name {first: string;second: string;}var name: Name;name = {first: 'John',second: 'Doe'};name = { // Error : `second` is missingfirst: 'John'};name = { // Error : `second` is the wrong typefirst: 'John',second: 1337};
Tutaj skomponowaliśmy adnotacje first: string
+ second: string
w nową adnotację Name
, która wymusza kontrolę typu na poszczególnych członkach. Interfejsy mają dużą moc w TypeScript i poświęcimy całą sekcję temu, jak możesz to wykorzystać na swoją korzyść.
Adnotacja typu inline
Zamiast tworzyć nową adnotację interface
, możesz dodać adnotację do wszystkiego, co chcesz inline, używając :{ /*Structure*/ }
. W poprzednim przykładzie ponownie przedstawiono typ inline:
var name: {first: string;second: string;};name = {first: 'John',second: 'Doe'};name = { // Error : `second` is missingfirst: 'John'};name = { // Error : `second` is the wrong typefirst: 'John',second: 1337};
Typy inline są świetne do szybkiego zapewnienia jednorazowej adnotacji typu dla czegoś. Oszczędza ci to kłopotów z wymyślaniem (potencjalnie złej) nazwy typu. Jednakże, jeśli znajdziesz się w umieszczaniu tej samej adnotacji typu inline wiele razy, dobrym pomysłem jest rozważenie refaktoryzacji do interfejsu (lub type alias
omówionego później w tej sekcji).
Typy specjalne
Poza typami prymitywnymi, które zostały omówione, istnieje kilka typów, które mają specjalne znaczenie w TypeScript. Są to any
, null
, undefined
, void
.
any
Typ any
zajmuje specjalne miejsce w systemie typów TypeScript. Daje ci on możliwość ucieczki z systemu typów, aby powiedzieć kompilatorowi, żeby się odwalił. any
jest kompatybilny z każdym i wszystkimi typami w systemie typów. Oznacza to, że wszystko może być do niego przypisane i może być przypisane do czegokolwiek. Jest to zademonstrowane w poniższym przykładzie:
var power: any;// Takes any and all typespower = '123';power = 123;// Is compatible with all typesvar num: number;power = num;num = power;
Jeśli przenosisz kod JavaScript do TypeScript, będziesz bliskim przyjacielem any
na początku. Jednak nie traktuj tej przyjaźni zbyt poważnie, ponieważ oznacza to, że to do Ciebie należy zapewnienie bezpieczeństwa typu. W zasadzie mówisz kompilatorowi, aby nie wykonywał żadnej sensownej analizy statycznej.
null i undefined
To, jak są one traktowane przez system typów, zależy od flagi kompilatora strictNullChecks
(omawiamy tę flagę później). Kiedy w strictNullCheck:false
, null
i undefined
literały JavaScript są efektywnie traktowane przez system typów tak samo jak coś typu any
. Te literały mogą być przypisane do dowolnego innego typu. Jest to zademonstrowane w poniższym przykładzie:
var num: number;var str: string;// These literals can be assigned to anythingnum = null;str = undefined;
:void
Użyj :void
do oznaczenia, że funkcja nie ma typu zwrotnego:
function log(message): void {console.log(message);}
Generics
Wiele algorytmów i struktur danych w informatyce nie zależy od rzeczywistego typu obiektu. Jednak nadal chcesz wymusić ograniczenia między różnymi zmiennymi. Prostym przykładem zabawki jest funkcja, która pobiera listę elementów i zwraca odwróconą listę elementów. Ograniczenie tutaj jest między tym, co jest przekazywane do funkcji, a tym, co jest zwracane przez funkcję:
function reverse<T>(items: T): T {var toreturn = ;for (let i = items.length - 1; i >= 0; i--) {toreturn.push(items);}return toreturn;}var sample = ;var reversed = reverse(sample);console.log(reversed); // 3,2,1// Safety!reversed = '1'; // Error!reversed = ; // Error!reversed = 1; // Okayreversed = ; // Okay
Tutaj w zasadzie mówisz, że funkcja reverse
pobiera tablicę (items: T
) jakiegoś typu T
(zauważ parametr typu w reverse<T>
) i zwraca tablicę typu T
(zauważ : T
). Ponieważ funkcja reverse
zwraca elementy tego samego typu, jaki przyjmuje, TypeScript wie, że zmienna reversed
jest również typu number
i zapewni Ci bezpieczeństwo typu. Podobnie, jeśli przekażesz tablicę string
do funkcji odwrotnej, zwrócony wynik jest również tablicą string
i otrzymasz podobne bezpieczeństwo typu, jak pokazano poniżej:
var strArr = ;var reversedStrs = reverse(strArr);reversedStrs = ; // Error!
W rzeczywistości tablice JavaScript mają już funkcję .reverse
i TypeScript rzeczywiście używa generycznych do określenia jej struktury:
interface Array<T> {reverse(): T;// ...}
To oznacza, że otrzymujemy bezpieczeństwo typu podczas wywoływania .reverse
na dowolnej tablicy, jak pokazano poniżej:
var numArr = ;var reversedNums = numArr.reverse();reversedNums = ; // Error!
Więcej na temat interfejsu Array<T>
omówimy później, gdy przedstawimy lib.d.ts
w rozdziale Deklaracje otoczenia.
Typ unii
Dość często w JavaScript chcesz pozwolić, aby właściwość była jednym z wielu typów np. a string
lub a number
. To jest miejsce, gdzie typ unii (oznaczany przez |
w adnotacji typu, np. string|number
) jest przydatny.
function formatCommandline(command: string|string) {var line = '';if (typeof command === 'string') {line = command.trim();} else {line = command.join(' ').trim();}// Do stuff with line: string}
Typ przecięcia
extend
jest bardzo częstym wzorcem w JavaScript, gdzie bierzemy dwa obiekty i tworzymy nowy, który ma cechy obu tych obiektów. Typ Intersection pozwala na użycie tego wzorca w bezpieczny sposób, jak pokazano poniżej:
function extend<T, U>(first: T, second: U): T & U {return { ...first, ...second };}const x = extend({ a: "hello" }, { b: 42 });// x now has both `a` and `b`const a = x.a;const b = x.b;
Typ Tuple
JavaScript nie posiada obsługi tupli pierwszej klasy. Ludzie generalnie po prostu używają tablicy jako tuple. To jest dokładnie to, co wspiera system typów TypeScript. Tuple mogą być oznaczane za pomocą :
itd. Tuple mogą mieć dowolną ilość członków. Tuple są zademonstrowane w poniższym przykładzie:
var nameNumber: ;// OkaynameNumber = ;// Error!nameNumber = ;
Połączenie tego z obsługą destrukturyzacji w TypeScript, tuple czują się dość pierwszą klasą, mimo że są tablicami pod spodem:
var nameNumber: ;nameNumber = ;var = nameNumber;
Alias typu
TypeScript zapewnia wygodną składnię do dostarczania nazw dla adnotacji typu, które chciałbyś użyć w więcej niż jednym miejscu. Aliasy są tworzone przy użyciu składni type SomeName = someValidTypeAnnotation
. Przykład jest zademonstrowany poniżej:
type StrOrNum = string|number;// Usage: just like any other notationvar sample: StrOrNum;sample = 123;sample = '123';// Just checkingsample = true; // Error!
W przeciwieństwie do interface
możesz nadać alias typu dosłownie każdej adnotacji typu (przydatne dla rzeczy takich jak typy unii i przecięcia). Oto kilka kolejnych przykładów, aby zapoznać cię ze składnią:
type Text = string | { text: string };type Coordinates = ;type Callback = (data: string) => void;
TIP: Jeśli potrzebujesz hierarchii adnotacji typu, użyj
interface
. Mogą być używane zimplements
iextends
TIP: Użyj aliasu typu dla prostszych struktur obiektów (jak
Coordinates
) tylko po to, aby nadać im semantyczną nazwę. Również, gdy chcesz nadać semantyczne nazwy typom Union lub Intersection, alias typu jest drogą do zrobienia.
Podsumowanie
Teraz, gdy możesz zacząć anotować większość swojego kodu JavaScript, możemy wskoczyć w najdrobniejsze szczegóły wszystkich mocy dostępnych w TypeScript’s Type System.