Zašto koristiti statičke tipove u JavaScript-u? (4-dijelni primer o statičnom tipkanju s Flow-om)

Kao programer za JavaScript, možete kodirati cijeli dan bez nailaženja na statičke tipove. Pa zašto se mučiti s učenjem o njima?

Pa ispada da tipovi učenja nisu samo vježba širenja uma. Ako ste spremni uložiti malo vremena u učenje o prednostima, nedostacima i slučajevima upotrebe statičkih tipova, to bi vam moglo neizmjerno pomoći u programiranju.

Zainteresiran? Pa, imate sreće - o tome govori ostatak ove četverodijelne serije.

Prvo, definicija

Najbrži način za razumijevanje statičkih tipova je njihovo suprotstavljanje dinamičkim tipovima. Jezik sa statičkim tipovima naziva se statički upisanim jezikom . S druge strane, jezik s dinamičkim tipovima naziva se dinamički tipkanim jezikom.

Suštinska razlika je u tome što statički tipizirani jezici vrše provjeru tipa u vrijeme kompajliranja , dok dinamički tipkani jezici vrše provjeru tipa tijekom izvođenja .

Ovo vam ostavlja još jedan koncept za rješavanje: što znači " provjera tipa" ?

Da bismo objasnili, pogledajmo tipove u Javi u odnosu na Javascript.

"Vrste" se odnosi na vrstu podataka koji se definiraju.

Na primjer, u Javi ako definirate booleankao:

boolean result = true;

Ovo je ispravan tip, jer se booleannapomena podudara s dodanom vrijednošću result, za razliku od cijelog broja ili bilo čega drugog.

S druge strane, ako ste pokušali izjaviti:

boolean result = 123;

... ovo se ne bi uspjelo prevesti jer ima neispravan tip. Izričito se označava resultkao a boolean, ali zatim definira kao cijeli broj 123.

JavaScript i drugi dinamički upisani jezici imaju drugačiji pristup, omogućavajući kontekstu da utvrdi koja se vrsta podataka definira:

var result = true;

Kratka priča: statički upisani jezici zahtijevaju da deklarirate tipove podataka svojih konstrukcija prije nego što ih možete koristiti. Dinamički upisani jezici ne. JavaScript podrazumijeva vrstu podataka, dok Java to izravno navodi.

Kao što vidite, tipovi vam omogućavaju da navedete invarijante programa ili logične tvrdnje i uvjete pod kojima će se program izvršavati.

Provjera tipa provjerava i nameće da se vrsta konstrukcije (konstanta, logička vrijednost, broj, varijabla, niz, objekt) podudara s invarijantom koju ste naveli. Na primjer, možete odrediti da "ova funkcija uvijek vraća niz." Kada se program izvodi, možete sigurno pretpostaviti da će vratiti niz.

Razlike između provjere statičkog tipa i dinamičke provjere tipa najvažnije su kada se dogodi pogreška tipa. U statički upisanom jeziku, pogreške u tipu javljaju se tijekom koraka kompilacije, odnosno u vrijeme sastavljanja. U jezicima s dinamičkim tipkanjem pogreške se javljaju samo kad se program izvrši. Odnosno, za vrijeme izvođenja .

To znači da se program napisan na dinamički upisanom jeziku (poput JavaScript-a ili Pythona) može kompajlirati čak i ako sadrži pogreške u tipu koje bi inače spriječile pravilno izvršavanje skripte.

S druge strane, ako program napisan na statički otkucanom jeziku (poput Scale ili C ++) sadrži pogreške tipa, neće se moći sastaviti dok greške ne budu ispravljene.

Nova era JavaScript-a

Budući da je JavaScript jezik koji se dinamički upisuje, možete se baviti deklariranjem varijabli, funkcija, objekata i bilo čega bez deklariranja vrste.

Praktično, ali ne uvijek idealno. Zbog toga su nedavno uskočili alati poput Flow i TypeScript koji programerima Java-a daju mogućnost * da koriste statičke tipove.

Flow je biblioteka za provjeru statičkih tipova otvorenog koda koju je razvio i objavio Facebook i omogućuje vam postupno dodavanje tipova u JavaScript kôd.

S druge strane, TypeScript je superset koji se kompilira na JavaScript - iako se osjeća gotovo kao novi jezik sa statičkim tipom . Ipak, izgleda i čini se vrlo sličnim JavaScriptu i nije ga teško pokupiti.

U oba slučaja, kada želite koristiti tipove, izričito kažete alatu koju datoteku (datoteke) treba provjeriti. Za TypeScript to radite tako da .tsumjesto .js. Pišete datoteke s nastavkom . Za Flow uključujete komentar na vrhu datoteke s@flow

Jednom kada ste izjavili da želite provjeriti datoteku, možete koristiti njihovu sintaksu za definiranje vrsta. Jednu razliku koju treba razlikovati između dva alata je ta da je Flow vrsta "provjeravatelja", a ne kompajler. S druge strane, TypeScript je prevodilac.

Doista vjerujem da alati poput Flow i TypeScript predstavljaju generacijski pomak i napredak za JavaScript.

Osobno sam toliko toga naučio koristeći tipove u svom svakodnevnom. Zbog toga se nadam da ćete mi se pridružiti na ovom kratkom i slatkom putovanju u statičke tipove.

Ostatak ovog 4-dijelnog posta pokrivat će:

Dio I. Kratki uvod u sintaksu i jezik Flow

Dijelovi II i III. Prednosti i nedostaci statičkih tipova (s detaljnim primjerima prolaska)

Dio IV. Trebate li koristiti statičke tipove u JavaScript-u ili ne?

Imajte na umu da sam odabrao Flow umjesto Typescripta u primjerima u ovom postu zbog svog poznavanja. Za svoje potrebe istražite i odaberite pravi alat za sebe. TypeScript je također fantastičan.

Bez daljnjega, krenimo!

1. dio: Kratki uvod u sintaksu i jezik Flow

Da biste razumjeli prednosti i nedostatke statičkih tipova, prvo biste trebali steći osnovno razumijevanje sintakse za statičke tipove koristeći Flow. Ako nikada prije niste radili na statički otkucanom jeziku, sintaksa će možda trebati neko vrijeme da se naviknete.

Počnimo s istraživanjem kako dodati tipove JavaScript primitivima, kao i konstrukcije poput Nizova, Objekta, Funkcija itd.

boolean

Ovo opisuje boolean(istinitu ili lažnu) vrijednost u JavaScript-u.

Primijetite da kada želite navesti vrstu, sintaksa koju koristite je:

broj

Ovo opisuje IEEE 754 broj s pomičnim zarezom. Za razliku od mnogih drugih programskih jezika, JavaScript ne definira različite vrste brojeva (poput cijelih brojeva, kratkih, dugih i plutajućih točaka). Umjesto toga, brojevi se uvijek pohranjuju kao brojevi s pomičnom zarezom dvostruke preciznosti. Dakle, za definiranje bilo kojeg broja potreban vam je samo jedan tip broja.

numberuključuje Infinityi NaN.

niz

Ovo opisuje niz.

null

Ovo opisuje nullvrstu podataka u JavaScript-u.

poništiti

Ovo opisuje undefinedvrstu podataka u JavaScript-u.

Imajte na umu da se nulli prema undefinednjima postupa drugačije. Ako ste pokušali učiniti:

Flow bi izbacio pogrešku jer bi tip voidtrebao biti tipa undefinedkoji nije isti kao tip null.

Niz

Opisuje JavaScript niz. Koristite sintaksu Array<; T> za opisivanje niza čiji su elementi neke vrste T.

Primijetite kako sam zamijenio Tsa string, što znači da se deklariram messageskao niz žica.

Objekt

Ovo opisuje JavaScript objekt. Postoji nekoliko različitih načina dodavanja vrsta objektima.

Možete dodati vrste da biste opisali oblik predmeta:

Objekte biste mogli definirati kao mape na kojima mapirate niz u neku vrijednost:

Objekt možete definirati i kao Objectvrstu:

Ovaj posljednji pristup omogućuje nam postavljanje bilo kojeg ključa i vrijednosti na vašem objektu bez ograničenja, tako da zapravo ne dodaje veliku vrijednost što se tiče provjere tipa.

bilo koji

Ovo može predstavljati doslovno bilo koju vrstu. anyTip je uspješno potvrđen, tako da bi trebao pokušati izbjeći ga koristiti bez prijeke potrebe (kao što su kada je potrebno isključiti iz tipa provjere ili trebate izlaz za nuždu).

Jedna situacija za koju biste mogli smatrati da je anykorisna je kada koristite vanjsku knjižnicu koja proširuje prototipove drugog sustava (poput Object.prototype).

Na primjer, ako koristite knjižnicu koja proširuje Object.prototype sa doSomethingsvojstvom:

Možete dobiti pogrešku:

Da biste to zaobišli, možete upotrijebiti any:

Funkcije

Najčešći način dodavanja vrsta funkcijama je dodavanje tipova ulaznim argumentima i (kada je relevantno) povratne vrijednosti:

Možete čak dodati vrste u funkcije asinkronizacije (vidi dolje) i generatore:

Primijetite kako je naš drugi parametar getPurchaseLimitoznačen kao funkcija koja vraća a Promise. I amountExceedsPurchaseLimitbilježi se kao i vraćanje a Promise.

Upišite zamjensko ime

Aliasing tipa jedan je od mojih najdražih načina korištenja statičkih tipova. Omogućuju vam upotrebu postojećih vrsta (broj, niz itd.) Za sastavljanje novih vrsta:

Iznad sam stvorio novi tip pod nazivom PaymentMethodkoji ima svojstva koja se sastoje od numberi stringtipova.

Sada ako želite koristiti PaymentMethodtip, možete učiniti:

Također možete stvoriti pseudonime tipa za bilo koji primitiv umotavanjem temeljnog tipa u drugi tip. Na primjer, ako želite unijeti alias a Namei EmailAddress:

Čineći to, ukazujete na to Namei Emailjesu različite stvari, a ne samo žice. Budući da ime i e-adresa zapravo nisu zamjenjivi, time se sprječavate da ih slučajno ne pomiješate.

Generički lijekovi

Generički su načini apstrahiranja samih tipova. Što to znači?

Pogledajmo:

Stvorio sam apstrakciju za tip T. Sada možete koristiti bilo koju vrstu koju želite predstavljati T. Jer numberT, Tbio je tipa number. U međuvremenu, jer arrayT, T je bio tipaArray er>.

Yes, I know. It’s dizzying stuff if this is the first time you’re looking at types. I promise the “gentle” intro is almost over!

Maybe

Maybe type allows us to type annotate a potentially null or undefined value. They have the type T|void|null for some type T, meaning it is either type T or it is undefined or null. To define a maybe type, you put a question mark in front of the type definition:

Here I’m saying that message is either a string, or it’s null or undefined.

You can also use maybe to indicate that an object property will be either of some type T or undefined:

By putting the ? next to the property name for middleInitial, you can indicate that this field is optional.

Disjoint unions

This is another powerful way to model data. Disjoint unions are useful when you have a program that needs to deal with different kinds of data all at once. In other words, the shape of the data can be different based on the situation.

Extending on the PaymentMethod type from our earlier generics example, let’s say that you have an app where users can have one of three types of payment methods. In this case, you can do something like:

Then you can define your PaymentMethod type as a disjoint union with three cases.

Payment method now can only ever be one of these three shapes. The property type is the property that makes the union type “disjoint”.

You’ll see more practical examples of disjoint union types later in part II.

All right, almost done. There are a couple other features of Flow worth mentioning before concluding this intro:

1) Type inference: Flow uses type inference where possible. Type inference kicks in when the type checker can automatically deduce the data type of an expression. This helps avoid excessive annotation.

For example, you can write:

Even though this Class doesn’t have types, Flow can adequately type check it:

Here I’ve tried to define area as a string, but in the Rectangle class definition we defined width and height as numbers. So based on the function definition for area, it must be return a number. Even though I didn’t explicitly define types for the area function, Flow caught the error.

One thing to note is that the Flow maintainers recommend that if you were exporting this class definition, you’d want to add explicit type definitions to make it easier to find the cause of errors when the class is not used in a local context.

2) Dynamic type tests: What this basically means is that Flow has logic to determine what the the type of a value will be at runtime and so is able to use that knowledge when performing static analysis. They become useful in situations like when Flow throws an error but you need to convince flow that what you’re doing is right.

I won’t go into too much detail because it’s more of an advanced feature that I hope to write about separately, but if you want to learn more, it’s worth reading through the docs.

We’re done with syntax

We covered a lot of ground in one section! I hope this high-level overview has been helpful and manageable. If you’re curious to go deeper, I encourage you to dive into the well-written docs and explore.

With syntax out of the way, let’s finally get to the fun part: exploring the advantages and disadvantages of using types!

Next up: Part 2 & 3.