Käsittelimme TypeScript-tyyppijärjestelmän tärkeimpiä ominaisuuksia käsitellessämme aiheen Miksi TypeScript?. Seuraavassa on muutama keskeinen ote tuosta keskustelusta, jotka eivät kaipaa enempää selitystä:
-
TypeScriptin tyyppijärjestelmä on suunniteltu valinnaiseksi niin, että JavaScriptisi on TypeScript.
-
TypeScript ei estä JavaScriptin emitointia tyyppivirheiden esiintyessä, jolloin voit asteittain päivittää JS:si TS:ksi.
Aloitetaanpa nyt TypeScript-tyyppijärjestelmän syntaksista. Näin voit heti alkaa käyttää näitä annotaatioita koodissasi ja nähdä hyödyn. Tämä valmistaa sinua syvällisempään sukellukseen myöhemmin.
Kuten aiemmin mainittiin, tyypit annotoidaan :TypeAnnotation
-syntaksilla. Mitä tahansa, mikä on käytettävissä tyyppi-ilmoitusavaruudessa, voidaan käyttää Type-annotaationa.
Seuraavassa esimerkissä demonstroidaan muuttujien, funktioparametrien ja funktioiden paluuarvojen tyyppi-annotaatioita:
var num: number = 123;function identity(num: number): number {return num;}
Primitiiviset tyypit
Javaskriptin primitiiviset tyypit ovat hyvin edustettuina TypeScriptin tyyppijärjestelmässä. Tämä tarkoittaa string
, number
, boolean
kuten alla on havainnollistettu:
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
Arraytit
TypeScript tarjoaa oman tyyppisyntaksin arrayille, mikä helpottaa koodin kommentointia ja dokumentointia. Syntaksi on periaatteessa :n lisääminen minkä tahansa kelvollisen tyyppihuomautuksen jälkeen (esim.
:boolean
). Sen avulla voit tehdä turvallisesti kaiken tavanomaisen array-käsittelyn ja se suojaa sinua virheiltä, kuten väärän tyyppisen jäsenen osoittamiselta. Tätä havainnollistetaan alla:
var boolArray: boolean;boolArray = ;console.log(boolArray); // trueconsole.log(boolArray.length); // 2boolArray = true;boolArray = ;boolArray = 'false'; // Error!boolArray = 'false'; // Error!boolArray = ; // Error!
Rajapinnat
Rajapinnat ovat TypeScriptin keskeinen tapa koota useita tyyppiannotaatioita yhdeksi nimetyksi annotaatioksi. Tarkastellaan seuraavaa esimerkkiä:
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};
Tässä olemme sommitelleet annotaatiot first: string
+ second: string
uudeksi annotaatioksi Name
, joka pakottaa tyyppitarkastukset yksittäisille jäsenille. Rajapinnoilla on paljon valtaa TypeScriptissä, ja omistamme kokonaisen kappaleen sille, miten voit käyttää sitä hyödyksesi.
Inline-tyypin annotaatio
Uuden interface
luomisen sijaan voit annotoida mitä tahansa haluamaasi inline-annotaatiota käyttämällä :{ /*Structure*/ }
. Edellisessä esimerkissä esitettiin taas inline-tyyppi:
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-tyypit ovat loistavia, kun haluat nopeasti antaa jollekin asialle kertaluonteisen tyyppihuomautuksen. Se säästää (mahdollisesti huonon) tyyppinimen keksimisen vaivalta. Jos kuitenkin huomaat laittavasi saman tyyppi-annotaation inline useita kertoja, on hyvä idea harkita sen refaktorointia rajapinnaksi (tai type alias
käsitellään myöhemmin tässä kappaleessa).
Erityistyypit
Käsiteltyjen primitiivityyppien lisäksi on muutamia tyyppejä, joilla on erityinen merkitys TypeScriptissä. Nämä ovat any
, null
, undefined
, void
.
kaikki
Tyypillä any
on erityinen paikka TypeScriptin tyyppijärjestelmässä. Se antaa sinulle pakoluukun tyyppijärjestelmästä, jolla voit käskeä kääntäjää painumaan helvettiin. any
on yhteensopiva kaikkien tyyppijärjestelmän tyyppien kanssa. Tämä tarkoittaa, että sille voidaan antaa mitä tahansa ja sille voidaan antaa mitä tahansa. Tätä havainnollistetaan alla olevassa esimerkissä:
var power: any;// Takes any and all typespower = '123';power = 123;// Is compatible with all typesvar num: number;power = num;num = power;
Jos olet siirtämässä JavaScript-koodia TypeScriptiin, olet alussa läheinen ystävä any
:n kanssa. Älä kuitenkaan ota tätä ystävyyttä liian vakavasti, sillä se tarkoittaa, että tyyppiturvallisuuden varmistaminen on sinun tehtäväsi. Käytännössä käsket kääntäjää olemaan tekemättä mitään mielekästä staattista analyysia.
null ja undefined
Miten tyyppijärjestelmä käsittelee niitä, riippuu kääntäjän strictNullChecks
lipusta (käsittelemme tätä lippua myöhemmin). Kun se on strictNullCheck:false
, tyyppijärjestelmä käsittelee null
– ja undefined
JavaScript-litteraaleja tehokkaasti samalla tavalla kuin jotain, jonka tyyppi on any
. Nämä litteraalit voidaan liittää mihin tahansa muuhun tyyppiin. Tämä osoitetaan alla olevassa esimerkissä:
var num: number;var str: string;// These literals can be assigned to anythingnum = null;str = undefined;
:void
Käytä :void
merkitäksesi, että funktiolla ei ole paluutyyppiä:
function log(message): void {console.log(message);}
Generics
Monet tietotekniikassa käytettävät algoritmit ja tietorakenteet eivät ole riippuvaisia objektin todellisesta tyypistä. Silti eri muuttujien välille halutaan silti asettaa jokin rajoitus. Yksinkertainen leluesimerkki on funktio, joka ottaa listan kohteita ja palauttaa käänteisen listan kohteita. Rajoitus tässä on sen välillä, mitä funktiolle syötetään ja mitä funktio palauttaa:
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
Tässä sanotaan periaatteessa, että funktio reverse
ottaa joukon (items: T
), joka on jotakin tyyppiä T
(huomaa tyyppiparametri reverse<T>
) ja palauttaa joukon, joka on tyyppiä T
(huomaa : T
). Koska reverse
-funktio palauttaa elementtejä, jotka ovat samaa tyyppiä kuin se ottaa, TypeScript tietää, että reversed
-muuttuja on myös tyypiltään number
, ja antaa tyyppivarmuuden. Vastaavasti jos annat käänteisfunktiolle string
-muotoisen matriisin, palautettu tulos on myös string
-muotoinen matriisi ja saat samanlaisen tyyppiturvallisuuden, kuten alla on esitetty:
var strArr = ;var reversedStrs = reverse(strArr);reversedStrs = ; // Error!
Tosiasiassa JavaScript-matriiseilla on jo .reverse
-funktio, ja TypeScript tosiaankin käyttää geneerisiä ominaisuuksia määrittelemään sen rakenteen:
interface Array<T> {reverse(): T;// ...}
Tämä tarkoittaa, että saat tyyppiturvallisuuden kutsuessasi .reverse
mitä tahansa arraya, kuten alla on esitetty:
var numArr = ;var reversedNums = numArr.reverse();reversedNums = ; // Error!
Keskustelemme lisää Array<T>
-rajapinnasta myöhemmin esitellessämme lib.d.ts
:n osiossa Ambient Declarations.
Union-tyyppi
Javaskriptissä halutaan melko yleisesti sallia, että ominaisuus voi olla yksi useammasta tyypistä esim. string
tai number
. Tällöin union-tyyppi (jota merkitään |
tyyppi-ilmoituksessa, esim. string|number
) on kätevä. Yleinen käyttötapaus on funktio, joka voi ottaa yhden objektin tai objektin joukon esim:
function formatCommandline(command: string|string) {var line = '';if (typeof command === 'string') {line = command.trim();} else {line = command.join(' ').trim();}// Do stuff with line: string}
Risteymätyyppi
extend
on hyvin yleinen malli JavaScriptissä, jossa otetaan kaksi objektia ja luodaan uusi, jolla on molempien objektien ominaisuudet. Intersection Type mahdollistaa tämän kuvion käytön turvallisella tavalla, kuten alla on esitetty:
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;
Tuple Type
JavaScriptissa ei ole ensimmäisen luokan tuple-tukea. Ihmiset käyttävät yleensä vain arraya tuplina. TypeScript-tyyppijärjestelmä tukee juuri tätä. Tuplet voidaan merkitä käyttämällä :
jne. Tuplilla voi olla kuinka monta jäsentä tahansa. Tuplia havainnollistetaan alla olevassa esimerkissä:
var nameNumber: ;// OkaynameNumber = ;// Error!nameNumber = ;
Yhdistämällä tämä TypeScriptin destrukturointitukeen, tuplat tuntuvat melko ensiluokkaisilta huolimatta siitä, että ne ovat alla olevia arrayjä:
var nameNumber: ;nameNumber = ;var = nameNumber;
Type Alias
TypeScript tarjoaa kätevän syntaksin, jolla voi antaa nimiä tyypin annotaatioille, joita haluaisi käyttää useammassa kuin yhdessä paikassa. Aliakset luodaan käyttämällä type SomeName = someValidTypeAnnotation
-syntaksia. Esimerkki on esitetty alla:
type StrOrNum = string|number;// Usage: just like any other notationvar sample: StrOrNum;sample = 123;sample = '123';// Just checkingsample = true; // Error!
Toisin kuin interface
voit antaa tyypin aliaksen kirjaimellisesti mille tahansa tyyppiannotaatiolle (hyödyllinen esimerkiksi union- ja intersection-tyypeille). Tässä vielä muutama esimerkki, jotta tutustut syntaksiin:
type Text = string | { text: string };type Coordinates = ;type Callback = (data: string) => void;
TIP: Jos tarvitset hierarkkisia Type-merkintöjä, käytä
interface
. Niitä voidaan käyttää yhdessäimplements
jaextends
kanssa
TIP: Käytä tyyppialiaksia yksinkertaisemmille objektirakenteille (kuten
Coordinates
) vain antaaksesi niille semanttisen nimen. Myös silloin, kun haluat antaa semanttisia nimiä Union- tai Intersection-tyypeille, Type alias on oikea tapa.
Yhteenveto
Nyt kun voit alkaa annotoida suurimman osan JavaScript-koodistasi, voimme siirtyä TypeScriptin Type Systemin tarjoamien mahdollisuuksien yksityiskohtiin.