Vi tog upp de viktigaste funktionerna i TypeScript Type System när vi diskuterade Varför TypeScript?. Nedan följer några viktiga behållningar från den diskussionen som inte behöver förklaras ytterligare:
-
Typsystemet i TypeScript är utformat för att vara valfritt så att ditt JavaScript är TypeScript.
-
TypeScript blockerar inte JavaScript emit i närvaro av typfel, vilket gör att du successivt kan uppdatera ditt JS till TS.
Nu börjar vi med syntaxen för TypeScript-typssystemet. På så sätt kan du börja använda dessa annotationer i din kod omedelbart och se fördelarna. Detta kommer att förbereda dig för en djupare dykning senare.
Som tidigare nämnts annoteras typer med hjälp av :TypeAnnotation
syntax. Allt som är tillgängligt i typdeklarationsutrymmet kan användas som en typannotation.
Följande exempel visar typannotationer för variabler, funktionsparametrar och funktionsreturvärden:
var num: number = 123;function identity(num: number): number {return num;}
Primitiva typer
De primitiva typerna i JavaScript är väl representerade i TypeScript-typsystemet. Detta innebär string
, number
, boolean
som demonstreras nedan:
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
Arrays
TypeScript tillhandahåller dedikerad typsyntax för arrays för att göra det lättare för dig att kommentera och dokumentera din kod. Syntaxen går i princip ut på att postfixera till vilken giltig typannotation som helst (t.ex.
:boolean
). Den gör det möjligt för dig att på ett säkert sätt göra all array-manipulation som du normalt skulle göra och skyddar dig från fel som till exempel att tilldela en medlem av fel typ. Detta demonstreras nedan:
var boolArray: boolean;boolArray = ;console.log(boolArray); // trueconsole.log(boolArray.length); // 2boolArray = true;boolArray = ;boolArray = 'false'; // Error!boolArray = 'false'; // Error!boolArray = ; // Error!
Gränssnitt
Gränssnitt är det centrala sättet i TypeScript att komponera flera typannotationer till en enda namngiven annotation. Tänk på följande exempel:
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};
Här har vi komponerat annotationerna first: string
+ second: string
till en ny annotation Name
som verkställer typkontrollerna på enskilda medlemmar. Gränssnitt har mycket kraft i TypeScript och vi kommer att ägna ett helt avsnitt åt hur du kan använda det till din fördel.
Inline Type Annotation
Istället för att skapa en ny interface
kan du annotera vad du vill inline med :{ /*Structure*/ }
. Föregående exempel presenterades återigen med en inline-typ:
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};
Inline-typer är utmärkta för att snabbt tillhandahålla en engångsannotation för något. Det besparar dig besväret med att komma på ett (potentiellt dåligt) typnamn. Men om du upptäcker att du sätter in samma typannotation inline flera gånger är det en bra idé att överväga att refaktorisera den till ett gränssnitt (eller en type alias
som behandlas senare i det här avsnittet).
Speciella typer
Bortom de primitiva typerna som har behandlats finns det några typer som har en speciell betydelse i TypeScript. Dessa är any
, null
, undefined
, void
.
any
Typen any
har en speciell plats i TypeScript-typssystemet. Den ger dig en flyktlucka från typsystemet för att tala om för kompilatorn att dra åt helvete. any
är kompatibel med alla typer i typsystemet. Det betyder att vad som helst kan tilldelas den och att den kan tilldelas vad som helst. Detta demonstreras i exemplet nedan:
var power: any;// Takes any and all typespower = '123';power = 123;// Is compatible with all typesvar num: number;power = num;num = power;
Om du porterar JavaScript-kod till TypeScript kommer du att vara nära vän med any
i början. Ta dock inte denna vänskap på alltför stort allvar eftersom den innebär att det är upp till dig att garantera typsäkerheten. Du säger i princip till kompilatorn att inte göra någon meningsfull statisk analys.
null och undefined
Hur de behandlas av typsystemet beror på kompilatorflaggan strictNullChecks
(vi tar upp denna flagga senare). När de står i strictNullCheck:false
behandlas null
och undefined
JavaScript-litteralerna i praktiken av typsystemet på samma sätt som något av typen any
. Dessa literaler kan tilldelas vilken annan typ som helst. Detta visas i nedanstående exempel:
var num: number;var str: string;// These literals can be assigned to anythingnum = null;str = undefined;
:void
Använd :void
för att ange att en funktion inte har någon returtyp:
function log(message): void {console.log(message);}
Generics
Många algoritmer och datastrukturer inom datavetenskapen beror inte på objektets faktiska typ. Men du vill ändå upprätthålla en begränsning mellan olika variabler. Ett enkelt leksaksexempel är en funktion som tar en lista med objekt och returnerar en omvänd lista med objekt. Begränsningen här är mellan det som skickas in till funktionen och det som returneras av funktionen:
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
Här säger du i princip att funktionen reverse
tar en array (items: T
) av en viss typ T
(lägg märke till typparametern i reverse<T>
) och returnerar en array av typen T
(lägg märke till : T
). Eftersom funktionen reverse
returnerar objekt av samma typ som den tar, vet TypeScript att variabeln reversed
också är av typen number
och ger dig typ-säkerhet. På samma sätt om du skickar in en array av string
till den omvända funktionen är det returnerade resultatet också en array av string
och du får liknande typsäkerhet som visas nedan:
var strArr = ;var reversedStrs = reverse(strArr);reversedStrs = ; // Error!
I själva verket har JavaScript-matriser redan en .reverse
-funktion och TypeScript använder faktiskt generics för att definiera dess struktur:
interface Array<T> {reverse(): T;// ...}
Detta innebär att du får typsäkerhet när du anropar .reverse
på en array som visas nedan:
var numArr = ;var reversedNums = numArr.reverse();reversedNums = ; // Error!
Vi kommer att diskutera mer om gränssnittet Array<T>
senare när vi presenterar lib.d.ts
i avsnittet Ambient Declarations.
Union Type
Ganska ofta i JavaScript vill man tillåta att en egenskap kan vara en av flera typer, t.ex. en string
eller en number
. Det är här som unionstypen (betecknad med |
i en typannotation, t.ex. string|number
) är användbar. Ett vanligt användningsfall är en funktion som kan ta ett enda objekt eller en array av objektet t.ex.:
function formatCommandline(command: string|string) {var line = '';if (typeof command === 'string') {line = command.trim();} else {line = command.join(' ').trim();}// Do stuff with line: string}
Intersektionstyp
extend
är ett mycket vanligt mönster i JavaScript där man tar två objekt och skapar ett nytt som har egenskaperna hos båda dessa objekt. Med en Intersection Type kan du använda det här mönstret på ett säkert sätt, vilket demonstreras nedan:
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;
Tupeltyp
JavaScript har inte stöd för första klassens tupel. Folk använder i allmänhet bara en array som en tupel. Detta är precis vad TypeScript-typ systemet stöder. Tupler kan kommenteras med hjälp av :
osv. En tupel kan ha valfritt antal medlemmar. Tuples demonstreras i nedanstående exempel:
var nameNumber: ;// OkaynameNumber = ;// Error!nameNumber = ;
Kombinera detta med destruktionsstödet i TypeScript, tuples känns ganska förstklassiga trots att de är matriser under:
var nameNumber: ;nameNumber = ;var = nameNumber;
Type Alias
TypeScript tillhandahåller en praktisk syntax för att ge namn till typannotationer som du vill använda på mer än ett ställe. Aliaserna skapas med hjälp av type SomeName = someValidTypeAnnotation
-syntaxen. Ett exempel visas nedan:
type StrOrNum = string|number;// Usage: just like any other notationvar sample: StrOrNum;sample = 123;sample = '123';// Just checkingsample = true; // Error!
Till skillnad från en interface
kan du ge ett typalias till bokstavligen vilken typannotation som helst (användbart för saker som union- och intersektionstyper). Här är ytterligare några exempel för att göra dig bekant med syntaxen:
type Text = string | { text: string };type Coordinates = ;type Callback = (data: string) => void;
TIP: Om du behöver ha hierarkier av typannotationer använder du en
interface
. De kan användas medimplements
ochextends
TIP: Använd ett typalias för enklare objektstrukturer (som
Coordinates
) bara för att ge dem ett semantiskt namn. Även när du vill ge semantiska namn till union- eller intersektionstyper är ett typalias rätt väg att gå.
Sammanfattning
Nu när du kan börja annotera det mesta av din JavaScript-kod kan vi hoppa in i de små detaljerna om all den kraft som finns tillgänglig i TypeScript’s Type System.