TypeScript för nybörjare, del 5 Generics

Den andra handledningen i vår TypScript för nybörjarserier fokuserade på grundläggande datatyper som finns tillgängliga i TypeScript. Typkontrollen i TypeScript låter oss se till att variablerna i vår kod endast kan ha specifika typer av värden som tilldelats dem. På så sätt kan vi undvika många misstag när vi skriver kod eftersom IDE kommer att kunna berätta för oss när vi utför en operation på en typ som den inte stöder. Detta gör typkontroll en av de bästa egenskaperna hos TypeScript.

I denna handledning kommer vi att fokusera på ett annat viktigt inslag i denna språkgenerik. Med generics kan du skriva SkrivScript som kan fungera på en mängd datatyper istället för att vara begränsad till en enda. Du kommer att lära dig mer om behovet av generics i detalj och hur det är bättre än att bara använda några datatyp som finns tillgänglig i TypeScript.

Behovet av generics

Om du inte är bekant med generics kanske du undrar varför vi behöver dem alls. I det här avsnittet kommer jag att svara på den här frågan för dig. Låt oss börja med att skriva en funktion som kommer att returnera ett slumpmässigt element från en rad tal.

funktionen randomIntElem (theArray: number []): number let randomIndex = Math.floor (Math.random () * theArray.length); returnera theArray [randomIndex];  låt positioner: nummer [] = [103, 458, 472, 458]; släpp randomPosition: number = randomIntElem (positioner);

De randomElem Funktionen som vi just definierat tar en rad siffror som enda parameter. Funktionens returtyp har också angivits som ett nummer. Vi använder Math.random () funktion för att returnera ett flytande punkts slumpmässigt tal mellan 0 och 1. Multiplicera det med längden på en given uppsättning och anrop Math.floor () på resultatet ger vi ett slumpmässigt index. När vi har slumpmässigt index returnerar vi elementet vid det specifika indexet.

Någon gång senare, låt oss säga att du behöver få ett slumpmässigt strängelement från en rad strängar. Vid denna tidpunkt kan du bestämma dig för att skapa en annan funktion som specifikt riktar sig till strängar.

funktion randomStrElem (theArray: string []): string let randomIndex = Math.floor (Math.random () * theArray.length); returnera theArray [randomIndex];  låt färger: sträng [] = ['violett', 'indigo', 'blå', 'grön']; släpp randomColor: string = randomStrElem (färger);

Vad händer om du behöver välja ett slumpmässigt element från en grupp av ett gränssnitt som du definierade? Att skapa en ny funktion varje gång du vill få ett slumpmässigt element från en rad olika objekt är inte möjlig.

En lösning för detta problem är att ange vilken typ av array-parameter som överförs till funktionerna som några[]. På så sätt kan du bara skriva din funktion en gång, och det kommer att fungera med en rad olika typer.

funktion randomElem (theArray: any []): any let randomIndex = Math.floor (Math.random () * theArray.length); returnera theArray [randomIndex];  låt positioner = [103, 458, 472, 458]; släpp randomPosition = randomElem (positioner); låt färger = ['violett', 'indigo', 'blå', 'grön']; släpp randomColor = randomElem (färger);

Som du kan se kan vi använda ovanstående funktion för att få slumpmässiga positioner och slumpmässiga färger. Ett stort problem med denna lösning är att du kommer att förlora informationen om vilken typ av värde som returneras. 

Tidigare var vi säkra på att randomPosition skulle vara ett nummer och slumpmässig färg skulle vara en sträng. Detta hjälpte oss att använda dessa värden i enlighet därmed. Nu är allt vi vet att det returnerade elementet kan vara av någon typ. I ovanstående kod kan vi ange vilken typ av slumpmässig färg att vara en siffra och får fortfarande inga fel.

// Denna kod kommer att kompileras utan ett fel. låt färger: sträng [] = ['violett', 'indigo', 'blå', 'grön']; släpp randomColor: number = randomElem (färger);

En bättre lösning för att undvika koddubbling medan du fortfarande behåller typinformationen är att använda generics. Här är en generisk funktion som returnerar slumpmässiga element från en array.

funktion randomElem(theArray: T []): T let randomIndex = Math.floor (Math.random () * theArray.length); returnera theArray [randomIndex];  låt färger: sträng [] = ['violett', 'indigo', 'blå', 'grön']; släpp randomColor: string = randomElem (färger);

Nu får jag ett fel om jag försöker ändra typen av slumpmässig färg från sträng till siffra. Detta visar att det är mycket säkrare att använda generics än att använda några skriv i sådana situationer.

Användning av generics kan ses mycket begränsande

I föregående avsnitt diskuterades hur du kan använda generics istället för några skriv för att skriva en enda funktion och undvika att uppnå fördelarna med typkontroll. Ett problem med den generiska funktionen som vi har skrivit i föregående avsnitt är att TypeScript inte låter oss utföra en hel del operationer på de variabler som passerat till den. 

Detta beror på att TypeScript inte kan göra några antaganden om typen av variabel som kommer att överföras till vår generiska funktion i förväg. Som ett resultat kan vår generiska funktion endast använda de operationer som är tillämpliga på alla datatyper. Följande exempel borde göra detta begrepp tydligare.

funktion removeChar (theString: strängen, theChar: strängen): sträng låt theRegex = ny RegExp (theChar, "gi"); returnera theString.replace (theRegex, ");

Ovanstående funktion kommer att ta bort alla händelser av den angivna tecknen från den givna strängen. Du kanske vill skapa en generell version av den här funktionen så att du också kan ta bort specifika siffror från ett visst tal såväl som tecken från en sträng. Här är motsvarande generiska funktion.

funktion removeIt(theInput: T, theIt: strängen): T låt theRegex = ny RegExp (theIt, "gi"); returnera theInput.replace (theRegex, ");

De removeChar funktionen visade inte dig ett fel. Men om du använder byta ut inuti ta bort den,TypeScript kommer att berätta det byta ut existerar inte för typ 'T'. Detta beror på att TypeScript inte längre kan anta det theInput kommer att bli en sträng.

Denna begränsning av att använda olika metoder i en generisk funktion kan leda till att du tror att begreppet generics inte kommer att vara till stor nytta. Det är inte så mycket att du kan göra med en handfull metoder som måste vara tillämpliga på alla datatyper för att du ska kunna använda dem inom en generisk funktion.

En viktig sak som du bör komma ihåg vid denna tidpunkt är att du inte generellt behöver skapa funktioner som kommer att användas med alla typer av datatyper. Det är vanligare att skapa en funktion som kommer att användas med en viss uppsättning eller olika datatyper. Denna begränsning av datatyper gör generiska funktioner mycket mer användbara.

Skapa generiska funktioner med hjälp av begränsningar

Den generiska ta bort den funktionen från föregående avsnitt visade ett fel eftersom byta ut Metoden inuti den är avsedd att användas med strängar, medan parametrarna som passerat till den kan ha vilken datatyp som helst. 

Du kan använda sträcker sökord för att begränsa datatyperna som överförs till en generisk funktion i TypeScript. dock, sträcker är begränsad till bara gränssnitt och klasser. Det betyder att de flesta generiska funktioner som du skapar kommer att ha parametrar som utökar ett basgränssnitt eller en klass. 

Här är en generisk funktion som skriver ut namnet på personer, familjemedlemmar eller kändisar som skickats till den.

gränssnitt Människor namn: sträng gränssnitt Familj namn: sträng, ålder: nummer, relation: sträng gränssnitt Celebrity sträcker folk yrke: sträng funktionen utskriftsnamn(theInput: T): void console.log ('Mitt namn är $ theInput.name');  låt serena: Celebrity = name: 'Serena Williams', yrke: 'Tennis Player' printName (serena);

I ovanstående exempel har vi definierat tre gränssnitt, och var och en har en namn fast egendom. Den generiska Skriv namn funktion som vi skapade kommer att acceptera något objekt som sträcker sig människor. Med andra ord kan du skicka antingen en familj eller ett kändisobjekt till den här funktionen, och det kommer att skriva ut sitt namn utan några klagomål. Du kan definiera många fler gränssnitt, och så länge de har en namn egendom, kommer du att kunna använda Skriv namn funktion utan problem.

Detta var ett mycket grundläggande exempel, men du kan skapa mer användbara generiska funktioner när du är mer bekväm med hela processen. Du kan till exempel skapa en generisk funktion som beräknar det totala värdet av olika föremål som säljs under en given månad så länge varje objekt har en pris egendom för att lagra priset och a såld egendom som lagrar antalet sålda varor. Med hjälp av generics kan du använda samma funktion så länge objekten utökar samma gränssnitt eller klass.

Slutgiltiga tankar

I denna handledning har jag försökt att täcka grunderna för generics i TypeScript på ett nybörjarevänligt sätt. Vi började artikeln genom att diskutera behovet av generika. Därefter lärde vi oss om rätt sätt att använda generics för att undvika koddubbling utan att offra typkontrollfunktionen. När du förstår grunderna som diskuteras här kan du läsa mer om generics i den officiella dokumentationen.

Om du har några frågor relaterade till denna handledning kommer jag gärna att svara på dem i kommentarerna.