Generieke programma's bieden herbruikbare stukjes code die werken met een aantal typen in plaats van één enkel type. Generieke programma's bieden een manier om het type als een variabele te behandelen en te specificeren bij gebruik, vergelijkbaar met functieparameters.
Generieken kunnen worden gebruikt in combinatie met functies (generieke functie maken), klassen (generieke klasse maken) en interfaces (generieke interface maken).
Basisgebruik
Je hebt in het verleden waarschijnlijk generics gebruikt zonder dat je dat wist - het meest voorkomende gebruik van generics is het declareren van een array:
const myArray: string[];
Op het eerste gezicht is het niet al te bijzonder, we verklaren gewoon mijnArray
als een array van tekenreeksen, maar het is hetzelfde als een generieke declaratie:
const mijnArray: Array;
Dingen expliciet houden
Laten we beginnen met een heel eenvoudig voorbeeld - hoe kunnen we deze vanille JS-functie overzetten naar TypeScript:
functie getPrefiledArray(vuller, lengte) {
return (nieuwe array(lengte)).fill(vuller);
}
Deze functie retourneert een array gevuld met een gegeven hoeveelheid vulstof
dus lengte
zullen nummer
en de hele functie zal een array van vulstof
- Maar wat is vulmiddel? Op dit punt kan het van alles zijn, dus één optie is om elke
:
functie getPrefiledArray(filler: elk, lengte: getal): elk[] {
return (new Array(length)).fill(filler);
}
Gebruik elke
is zeker generiek - we kunnen letterlijk alles doorgeven, dus "werken met een aantal typen in plaats van een enkel type" uit de definitie is volledig gedekt, maar we verliezen het verband tussen vulstof
type en het retourneertype. In dit geval willen we een gemeenschappelijk ding retourneren, en we kunnen dit gemeenschappelijke ding expliciet definiëren als typeparameter:
functie getPrefiledArray(vuller: T, lengte: getal): T[] {
return (new Array(length)).fill(filler);
}
en gebruik het op deze manier:
const prefilledArray = getPrefiledArray(0, 10);
Algemene beperkingen
Laten we eens kijken naar andere, waarschijnlijk meer voorkomende gevallen. Waarom gebruiken we eigenlijk types in functies? Voor mij is het om ervoor te zorgen dat argumenten die aan de functie worden doorgegeven, eigenschappen hebben waarmee ik wil interageren.
Laten we nogmaals proberen om een eenvoudige vanille JS-functie te porten naar TS.
functie getLength(ding) {
return thing.length;
}
We hebben een niet-triviaal raadsel - hoe kunnen we ervoor zorgen dat het ding een lengte
eigenschap, en de eerste gedachte kan zijn om iets te doen als:
functie getLength(thing: typeof Array):getal {
return thing.length;
}
en afhankelijk van de context kan het correct zijn, over het algemeen zijn we een beetje generiek - het zal werken met arrays van meerdere types, maar wat als we niet echt weten of het ding altijd een array moet zijn - misschien is het ding een voetbalveld of een bananenschil? In dat geval moeten we de algemene eigenschappen van dat ding verzamelen in een construct dat eigenschappen van een object kan definiëren - een interface:
interface IThingWithLength {
lengte: getal;
}
We kunnen IThingWithLength
interface als type van de ding
parameter:
functie getLength(thing: IThingWithLength):getal {
return thing.length;
}
in dit eenvoudige voorbeeld is het prima, maar als we dit type generiek willen houden en niet geconfronteerd willen worden met het probleem uit het eerste voorbeeld, kunnen we het volgende gebruiken Algemene beperkingen:
functie getLength(thing: T):getal {
return thing.length;
}
en gebruik het:
interface IBananaPeel {
dikte: getal;
lengte: getal;
}
const bananaPeel: IBananaPeel = {dikte: 0,2, lengte: 3,14};
getLength(bananaPeel);
Gebruik breidt uit
zorgt ervoor dat T
zal eigenschappen bevatten die zijn gedefinieerd door IThingWithLength
.
Generieke klassen
Tot nu toe werkten we met generieke functies, maar dat is niet de enige plek waar generieke functies schitteren, laten we eens kijken hoe we ze kunnen opnemen in klassen.
Laten we eerst proberen om een tros bananen op te slaan in de bananenkorf:
Klasse Banaan {
constructeur(
openbare lengte: getal,
publieke kleur: string,
openbare ioniserendeStraling: getal
) {}
}
klasse bananenmand {
private bananen: Banaan[] = [];
add(banaan: Banaan): void {
this.bananas.push(banana);
}
}
const bananaBasket = new BananaBasket();
bananaBasket.add(new Banana(3.14, 'red', 10e-7));
Laten we nu proberen om een mand voor algemene doeleinden te maken, voor verschillende dingen met hetzelfde type:
klasse Mand {
privé spullen: T[] = [];
toevoegen(ding: T): leeg {
this.stuff.push(ding);
}
}
const bananaBasket = nieuw Mandje();
En tot slot, laten we aannemen dat onze mand een container voor radioactief materiaal is en dat we alleen materie kunnen opslaan die ioniserendestraling
eigendom:
interface IRadioactive {
ionizingRadiation: number;
}
klasse RadioactiveContainer {
private spullen: T[] = [];
toevoegen(ding: T): leeg {
this.stuff.push(thing);
}
}
Algemene interface
Laten we tot slot proberen om al onze kennis te verzamelen en een radioactief rijk te bouwen door ook Generieke Interfaces te gebruiken:
// Gemeenschappelijke kenmerken voor containers definiëren
interface IRadioactive {
ioniserendeStraling: getal;
}
// Definieer iets dat radioactief is
interface IBanana uitbreidt IRadioactief {
lengte: getal;
kleur: tekenreeks;
}
// Definieer iets dat niet radioactief is
interface IDog {
gewicht: getal;
}
// Definieer interface voor container die alleen radioactieve dingen kan bevatten
interface IRadioactiveContainer {
add(ding: T): leeg;
getRadioactive(): getal;
}
// Definieer klasse die de interface van de radioactieve container implementeert
klasse RadioactiveContainer implementeert IRadioactiveContainer {
Privé: T[] = [];
toevoegen(ding: T): leeg {
this.stuff.push(thing);
}
getRadioactiveness(): getal {
return this.stuff.reduce((a, b) => a + b.ioniserendestraling, 0)
}
}
// FOUT! Type 'IDog' voldoet niet aan de beperking 'IRadioactive'.
// En het is nogal brutaal om honden op te slaan in een radioactieve container
const dogsContainer = nieuwe RadioactiveContainer();
// Alles goed, fam!
const radioactiveContainer = nieuwe RadioactiveContainer();
// Vergeet niet je radioactieve afval te sorteren - maak een aparte bak voor alleen bananen
const bananasContainer = nieuwe RadioactiveContainer();
Dat is alles mensen!