Generics og augmentation vil gøre dig til en TypeScript-guide

Foto af Michal Lomza på Unsplash

Dette er det andet indlæg i min TypeScript-serie; den første er her.

Som jeg nævnte før, er TypeScript fantastisk. Da jeg kun var begyndt at kode i TS, elskede jeg det frie og tilladte sprogets natur - jo mere du giver, jo mere får du. Jeg brugte kun type annoteringer hver nu og da. Nogle gange fik jeg dejlige autofuldførelser og tip fra compileren, men det gjorde jeg stort set ikke.

Med tiden lærte jeg, at hver gang jeg omgå kompilatoren, oprettede jeg en runtime-fejl, der ventede på at ske. Hver gang jeg dryssede lidt som enhver for at få fejlene til at forsvinde, betalte jeg for det senere med timer med omhyggelig fejlsøgning.

Så jeg lærte ikke at gøre det. Jeg begyndte at blive venner med compileren og være opmærksom, da den blev irriteret af koden, jeg skrev. Compileren ser vores mangler og advarer os, inden vi kan forårsage nogen reel skade. Som udvikler, indså jeg, at compileren er den bedste ven, jeg nogensinde har haft, fordi den beskytter mig mod mig selv.

”Det kræver meget mod at stå op mod vores fjender, men lige så meget for at stå op til vores venner.” A. P. Dumbledore

Uanset hvor god en ven kompileren måtte være, er det ikke altid let at behage. At undgå enhver kan være vanskelig fra tid til anden, og nogle gange ser det ud til, at enhver er den eneste rimelige løsning. I denne artikel vil jeg tackle to af disse sager og vise dig, hvordan du undgår enhver, og få en kode, der er typesikker, genanvendelig og selvforklarende.

Brug af Generics

Lad os sige, at vi arbejder med en skoledatabase, og vi har skrevet en dejlig hjælpefunktion kaldet getBy. For at få et studentobjekt ved navn kan vi køre getBy (model, "navn", "Harry"). Lad os se på, hvordan dette kan se ud (af enkelthedens skyld implementerede jeg DB-modellen som en enkel matrix).

Så vi har en anstændig funktion, men den har ingen typeanmærkninger, og ingen typer betyder ingen sikkerhed - så lad os løse det:

Det er meget bedre! Nu kender vores compiler typen af ​​resultatet, og dette vil hjælpe os senere. For at opnå denne type sikkerhed ofrede vi genanvendeligheden af ​​vores funktion. Hvad hvis vi vil bruge det til at hente andre enheder i fremtiden? Der skal være en bedre måde.

Og der er! I TypeScript, som i andre stærkt indtastede sprog, kan vi bruge generiske typer. En generisk er som en variabel, men i stedet for at have en værdi, indeholder den en typedefinition. Lad os refaktor vores funktion til at bruge en generisk type T i stedet for Student.

Sød! Nu kan vores funktion genbruges, og vi nyder stadig typen sikkerhed. Bemærk, hvordan på linje 5 informerer jeg eksplicit kompilatoren om, at jeg vil bruge typen Student som den generiske T - det gjorde jeg for klarhedens skyld, men kompilatoren kan faktisk udlede det alene, så det vises ikke i de næste eksempler.

Nu har vi en solid genanvendelig funktion, men vi kan stadig gøre det bedre. Hvad sker der, hvis jeg opretter en skrivefejl og skriver "naem" som min anden parameter? Det mislykkes lydløst. Mit system fungerer som om denne studerende ikke findes, og jeg vil bruge timer på fejlsøgning.

For at ordne det, vil jeg introducere en ny generisk type P. Jeg vil have, at P skal være en nøgle af typen T, så hvis jeg brugte Student, vil jeg, at P skal være "navn", "alder" eller "hasScar". Sådan kan det gøres:

Brug af generiske typer og med keyof er en ekstremt kraftig teknik. Hvis du koder i en IDE, der understøtter TypeScript, får du autofuldførelse, mens du skriver argumenterne, hvilket er meget praktisk.

Men vi er ikke færdige endnu. Der er stadig det tredje argument for vores funktion, der sidder der typeløst - dette er uacceptabelt. Indtil nu havde vi ingen måde at vide på forhånd, hvilken type det skulle være, da det afhænger af hvad vi passerer som det andet argument. Men nu, hvor vi har typen P, kan vi dynamisk udlede den. Typen af ​​det tredje argument er derfor T [P], så hvis T står for Student og P står for "alder", vil T [P] være af typenummer.

På dette tidspunkt håber jeg, at du har en krystalklar forståelse af brugen af ​​generiske materialer, men hvis du vil lege omkring dig selv, skal du gå videre og tjek det fulde kodeeksempel, jeg skrev på TypeScript-legepladsen her.

Forøgelse af eksisterende typer

Undertiden konfronteres vi muligvis behovet for at tilføje data eller funktionalitet til grænseflader, der er ude af vores kontrol. Det kan være, at du ønsker at ændre et standardobjekt, som at tilføje en egenskab til vindueobjektet eller øge adfærden i et eksternt bibliotek som Express. I begge tilfælde kan du ikke ændre typedefinitionen på de objekter, du arbejder på direkte.

Til denne demonstration tilføjer vi vores funktion getBy til Array's prototype, så vi får en renere syntaks. Hvorvidt dette er en god idé eller ej, er ikke relevant på dette tidspunkt, da det, vi efterspørger, er at lære teknikken.

Når vi prøver at tilføje vores funktion til Array-prototypen, kan vi se, at kompilatoren bliver vred på os:

Hvis vi forsøger at tilfredsstille kompilatoren ved at skrive som enhver her og der, har vi mistet alt det, vi arbejdede for - kompilatoren lukker, men ikke mere sikkerhed for os.

En bedre fremgangsmåde ville være at udvide typen Array, men først lad os tale om, hvordan TypeScript håndterer sager om to grænseflader med den samme type. Svaret er enkelt - hvis det er muligt, flettes det definitionerne, hvis ikke, vil det rejse en fejl.

Så dette fungerer:

Og dette gør ikke:

Nu hvor vi forstår det, står vi over for en ret nem opgave. Alt hvad vi skal gøre er at erklære en grænseflade Array og tilføje funktionen getBy til den.

Vigtig note: Det meste af tiden koder du sandsynligvis i modulfiler, så for at ændre Array-interface skal du få adgang til det globale omfang. Dette kan let gøres ved at placere din typedefinition inde i erklære global, som denne:

Hvis du ønsker at udvide en grænseflade til et eksternt bibliotek, skal du sandsynligvis have adgang til dette biblioteks navneområde. Her er et eksempel på, hvordan man tilføjer et userId-felt til Express's Request:

Så… det handler om det. Som før kan du lege med eksemplets kode her for at få mere selvtillid.

Du ved hvad du skal gøre nu ...

Hvis du er kommet så langt og lært noget nyt, kan du vise din påskønnelse ved at klappe for denne artikel et par dusin gange, så andre mennesker vil se den og lære også. Og hvis du gerne vil læse mere om det, jeg skriver, skal du gå videre og trykke på knappen 'Følg'.

gå ikke glip af mit næste indlæg, som handler om TypeScript-dekoratører!