Introducere în TypeScript
Publicat: 2020-10-29Primele limbaje cu care am programat vreodată sunt C și Java. Cu aceste limbi, de fiecare dată când definiți o variabilă trebuie să specificați tipul acesteia. Dacă încercați să îi atribuiți o valoare de alt tip, compilatorul se va plânge:
// main.c int main() { int x = "two"; return 0; } > cc main.c -o main // warning: initialization makes integer from pointer without a castProblema este că, din cauza lipsei mele de experiență, multe dintre plângerile primite de la compilator păreau criptic și complexe. De aceea, până la urmă, am privit compilatorul ca pe un instrument care îmi limita creativitatea, când în realitate ar trebui să fie un partener care este acolo să ajute.

Mai târziu, în cariera mea, am început să folosesc unele limbaje de programare fără tastare puternică, cum ar fi JavaScript sau PHP. Mi s-au părut destul de mișto: a fost extrem de ușor să prototipezi lucrurile rapid fără a avea de-a face cu un compilator pretențios.
După cum știți deja, limbajele de programare pe care se bazează WordPress sunt PHP și JavaScript. Aceasta înseamnă că probabil că sunteți obișnuit să vă codificați pluginurile și temele fără ca un compilator să vă supravegheze spatele. Ești doar tu, abilitățile tale și creativitatea ta. Ei bine, și erori ca aceasta:
const user = getUser( userId ); greet( user.name ); // Uncaught TypeError: user is undefined Dacă v-ați săturat de erori de tip undefined în codul dvs., este timpul să adăugați un compilator la fluxul de lucru. Să aruncăm o privire la ce este TypeScript și cum ne permite să îmbunătățim calitatea software-ului nostru cu mai multe ordine de mărime.
Ce este TypeScript
TypeScript este un limbaj de programare bazat pe JavaScript care a fost creat cu scopul de a adăuga tastare puternică și statică. Tipurile TypeScript ne permit să descriem forma obiectelor și variabilelor noastre, rezultând un cod mai bine documentat și mai robust. TypeScript însuși va fi responsabil pentru validarea tot ceea ce facem.
Așa cum a fost proiectat, TypeScript este un superset de JavaScript. Aceasta înseamnă că orice cod scris în JavaScript simplu este, prin definiție, și TypeScript valid. Dar inversul nu este adevărat: dacă utilizați caracteristici specifice TypeScript, codul rezultat nu este JavaScript valid până când nu îl transpilați.
Cum funcționează TypeScript
Pentru a înțelege cum funcționează TypeScript, vom folosi Playground -ul său, un editor mic în care putem scrie cod TypeScript și să vedem ce ne spune compilatorul despre el.
Fiind un superset de JavaScript, scrierea codului TypeScript este extrem de ușoară. Practic următorul cod JavaScript:
let user = "David"; let age = 34; let worksAtNelio = true; console.log( user, age, worksAtNelio );este, de asemenea, cod TypeScript. Îl puteți copia și lipi în TypeScript Playground și veți vedea că se compilează. Deci, ce este atât de special la asta? Tipurile sale, desigur. În JavaScript, puteți face următoarele:
let user = "David"; let age = 34; let worksAtNelio = true; user = { name: "Ruth" }; console.log( user, age, worksAtNelio );dar asta va declanșa o eroare în TypeScript. Încercați-l în Playground și veți vedea următoarea eroare:
Type '{ name: string; }' is not assignable to type 'string'.Deci despre ce este vorba?
TypeScript poate deduce automat tipul unei variabile. Asta înseamnă că nu trebuie să îi spunem în mod explicit „hei, această variabilă este un șir” (sau un număr, sau un boolean, sau orice altceva); în schimb, se uită la valoarea care ia fost dată pentru prima dată și deduce tipul acesteia pe baza acesteia.

În exemplul nostru, când am definit user variabil, i-am atribuit șirul de text "David" , astfel încât TypeScript știe că user este (și ar trebui să fie întotdeauna) un șir . Problema este că puțin mai târziu încercăm să schimbăm tipul variabilei noastre de user atribuindu-i un obiect care are o singură proprietate ( name ) a cărei valoare este șirul "Ruth" . În mod clar, acest obiect nu este un șir , așa că TypeScript se plânge și ne spune că atribuirea nu poate fi efectuată.
Există două căi posibile pentru a remedia acest lucru:
// Option 1 let user = "David"; user = "Ruth"; // OK. // Option 2 let user = { name: "David" }; user = { name: "Ruth" };Să fim expliciți atunci când definim tipurile variabilelor noastre
Dar inferența de tip este acolo doar pentru a ne ajuta. Dacă vrem, putem spune explicit TypeScript tipul unei variabile:
let user: string = "David"; let age: number = 34; let worksAtNelio: boolean = true; console.log( user, age, worksAtNelio );care redă exact același rezultat ca și tipurile deduse de TypeScript. Acum, aceasta ridică întrebarea: dacă TypeScript poate deduce tipuri automat, de ce avem nevoie de această caracteristică?
Pe de o parte, specificarea în mod explicit a tipurilor poate servi pentru a documenta mai bine codul nostru (care va deveni și mai clar în secțiunea următoare). Pe de altă parte, ne permite să rezolvăm limitările sistemului de inferență de tip. Da, ai citit bine: sunt cazuri în care inferența nu este foarte bună și cel mai bun TypeScript pe care îl poate face este să ne spună că „ei bine, nu știu care ar trebui să fie exact această variabilă; Presupun că poate fi orice!”
Luați în considerare următorul exemplu:
const getName = ( person ) => person.name; let david = { name: "David", age: 34 }; console.log( getName( david ) ); // Parameter 'person' implicitly has an 'any' type. În acest caz, definim o funcție getName care primește un parametru și returnează o valoare. Observați câte lucruri presupunem într-o funcție atât de simplă: ne așteptăm la un obiect ( person ) cu cel puțin o proprietate numită name . Dar nu avem nicio garanție că oricine apelează această funcție o va folosi corect. Dar chiar dacă o fac, încă nu știm care este tipul rezultat al acestei funcții. Sigur, ai putea presupune că un nume va fi un șir , dar știi asta doar pentru că ești om și înțelegi sensul acelui cuvânt. Dar uitați-vă la următoarele exemple:

getName( "Hola" ); // Error getName( {} ); // Returns undefined getName( { name: "David" } ); // Returns a string getName( { name: true } ); // Returns a booleande fiecare dată când apelăm funcția, obținem un rezultat diferit! Deci, în ciuda faptului că TypeScript ne urmărește, încă ne confruntăm cu probleme clasice cu JavaScript: această funcție poate primi orice și poate returna orice.

Soluția, ați ghicit-o, este să adnotați în mod explicit funcția.
Definiți-vă propriile tipuri
Dar înainte de a o face, să vedem o altă funcție suplimentară TypeScript: tipuri de date personalizate. Până acum, aproape toate exemplele noastre au folosit tipuri de bază precum șir , număr , boolean etc., care cred că sunt destul de ușor de înțeles. Am văzut, de asemenea, structuri de date mai complexe, cum ar fi obiecte:
let david = { name: "David", age: 34 };dar nu am acordat prea multă atenție tipului său așa cum este dedus de TypeScript, nu-i așa? Tot ce am văzut este un exemplu de atribuire nevalidă din cauza unei nepotriviri de tastare:
let user = "David"; user = { name: "Ruth" }; // Type '{ name: string; }' is not assignable to type 'string'.care a sugerat cumva că tipul obiectului a fost „ {name:string;} ”, pe care îl puteți citi ca „acesta este un obiect cu o proprietate numită nume de tip șir ”. Dacă așa sunt definite tipurile de obiecte, atunci următoarele ar trebui să fie valide TypeScript:
let david: { name: string, age: number } = { name: "David", age: 34 };si chiar este. Dar sunt sigur că veți fi de acord că nu este convenabil. Din fericire, putem crea noi tipuri în TypeScript.
În general, un tip personalizat este practic un tip TypeScript obișnuit cu un nume personalizat:
type Person = { name: string; age: number; }; let david: Person = { name: "David", age: 34 }; Tare nu? Acum că avem tipul Persoană , putem adnota cu ușurință funcția noastră getName și să specificăm clar tipul parametrului nostru de intrare:
const getName = ( person: Person ) => person.name; Și această actualizare simplă oferă TypeScript o mulțime de informații! De exemplu, acum poate deduce că tipul de rezultat al acestei funcții este un șir , deoarece știe cu siguranță că atributul name al unui obiect Person este și un șir :
let david: Person = { name: "David", age: 34 }; let davidName = getName( david ); davidName = 2; // Type 'number' is not assignable to type 'string'.Dar, ca întotdeauna, puteți adnota în mod explicit tipul de rezultat dacă doriți:
const getName = ( person: Person ): string => person.name;Mai multe chestii cu tipuri TypeScript...
În cele din urmă, am vrut să vă împărtășesc câteva fapte interesante de care puteți beneficia dacă vă definiți propriile tipuri în TypeScript.
TypeScript este foarte solicitant când vine vorba de alocarea de valori variabilelor. Dacă ați definit în mod explicit tipul unei anumite variabile, puteți aloca doar valori care se potrivesc exact cu acel tip. Să vedem cu câteva exemple:
type Person = { name: string; age: number; }; let david: Person = { name: "David", age: 34 }; // OK let ruth: Person = { name: "Ruth" }; // Property 'age' is missing in type '{ name: string; }' but required in type 'Person' let toni: Person = { name: "Toni", age: 35, gender: "M" }; // Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'. Object literal may only specify known properties, and 'gender' does not exist in type 'Person'. După cum puteți vedea, o variabilă Persoană acceptă numai obiecte care respectă pe deplin tipul Persoană . Dacă lipsesc unele atribute ( ruth nu are o age ) sau există mai multe atribute decât cele incluse în tip ( toni are un gender ), TypeScript se va plânge și va declanșa o eroare de nepotrivire de tip.
Dar, dacă vorbim despre invocarea funcțiilor, lucrurile stau altfel! Tipurile de argument dintr-o funcție nu sunt o cerință exactă, dar specifică interfața minimă pe care trebuie să o respecte parametrii. Știu, știu, e prea abstract. Să aruncăm o privire la următorul exemplu, care cred că va clarifica lucrurile:
type NamedObject = { name: string; }; const getName = ( obj: NamedObject ): string => obj.name; const ruth = { name: "Ruth" } getName( ruth ); // OK const david = { name: "David", age: 34 }; getName( david ); // OK const toni = { firstName: "Toni" }; getName( toni ); // Argument of type '{ firstName: string; }' is not assignable to parameter of type 'NamedObject'. Property 'name' is missing in type '{ firstName: string; }' but required in type 'NamedObject'. După cum puteți vedea, am redefinit funcția getName ca ceva mai generic: acum este nevoie de un obiect NamedObject , adică un obiect care trebuie să aibă un atribut numit name de tip șir . Folosind această definiție, vedem cum ruth și david se potrivesc perfect cu aceste cerințe (amândoi au un atribut name ), dar toni nu are, deoarece nu are atributul expecte name .
Concluzie
TypeScript este un limbaj de programare care extinde JavaScript prin adăugarea de definiții de tip statice. Acest lucru ne permite să fim mult mai precisi atunci când definim datele cu care lucrăm și, mai important, ne ajută să detectăm erorile mai devreme.
Costul integrării TypeScript într-o stivă de dezvoltare este relativ mic și poate fi realizat treptat. Deoarece tot codul JavaScript este, prin definiție, TypeScript, trecerea de la JavaScript la TypeScript este automată – puteți adăuga tipuri și vă puteți înfrumuseța codul pas cu pas.
Dacă ți-a plăcut această postare și vrei să afli mai multe, te rog să o distribui și să-mi spui în secțiunea de comentarii de mai jos.
Imagine prezentată de King's Church International pe Unsplash.
