Geneeriset ohjelmat tarjoavat uudelleenkäytettäviä koodinpätkiä, jotka toimivat useiden tyyppien kanssa yhden tyypin sijasta. Generics tarjoaa tavan käsitellä tyyppiä muuttujana ja määritellä se käytön yhteydessä, kuten funktioparametrit.
Geneerisiä ominaisuuksia voidaan käyttää yhdessä funktioiden (geneeristen funktioiden luominen), luokkien (geneeristen luokkien luominen) ja rajapintojen (geneeristen rajapintojen luominen) kanssa.
Peruskäyttö
Olet luultavasti käyttänyt geneerisiä ominaisuuksia aiemmin jopa tietämättäsi - yleisin geneeristen ominaisuuksien käyttö on matriisin julistaminen:
const myArray: string[];
Se ei ole liian erikoinen ensi silmäyksellä, me vain julistaa - myArray
merkkijonojen joukkona, mutta se on sama kuin yleinen ilmoitus:
const myArray: Array;
Pidetään asiat selkeinä
Aloitetaan hyvin yksinkertaisella esimerkillä - miten voisimme siirtää tämän vanilla JS-funktion TypeScript:hen:
function getPrefiledArray(filler, length) {
return (new Array(length)).fill(filler);
}
Tämä funktio palauttaa array täynnä tietyn määrän täyteaine
, joten pituus
on numero
ja koko funktio palauttaa array of täyteaine
- mutta mitä on täyteaine? Tässä vaiheessa se voi olla mitä tahansa, joten yksi vaihtoehto on käyttää täytettä. kaikki
:
function getPrefiledArray(filler: any, length: number): any[] {
return (new Array(length)).fill(filler);
}
Käyttämällä kaikki
on varmasti yleinen - voimme siirtää kirjaimellisesti mitä tahansa, joten määritelmän "työskentely useiden tyyppien kanssa yhden tyypin sijasta" on täysin katettu, mutta menetämme yhteyden välillä täyteaine
tyyppi ja paluutyyppi. Tässä tapauksessa haluamme palauttaa jonkin yhteisen asian, ja voimme määritellä tämän yhteisen asian nimenomaisesti muotoon tyyppiparametri:
function getPrefiledArray(filler: T, length: number): T[] {
return (new Array(pituus)).fill(filler);
}
ja käytä näin:
const prefilledArray = getPrefiledArray(0, 10);
Yleiset rajoitukset
Tarkastellaan erilaisia, todennäköisesti yleisempiä tapauksia. Miksi käytämme tyyppejä funktioissa? Minulle se on sen varmistamista, että funktiolle annetuilla argumenteilla on joitakin ominaisuuksia, joiden kanssa haluan olla vuorovaikutuksessa.
Yritetään jälleen kerran siirtää yksinkertainen vanilla JS-funktio TS:ään.
funktio getLength(asia) {
return thing.length;
}
Meillä on ei-triviaali pulma - miten varmistaa, että asia on pituus
ominaisuutta, ja ensimmäinen ajatus voi olla tehdä jotain sellaista kuin:
function getLength(thing: typeof Array):number {
return thing.length;
}
ja asiayhteydestä riippuen se saattaa olla oikein, kaiken kaikkiaan olemme hieman geneerisiä - se toimii useiden eri tyyppisten matriisien kanssa, mutta entä jos emme todellakaan tiedä, pitäisikö asian olla aina matriisi - ehkä asia on jalkapallokenttä tai banaaninkuori? Tällöin meidän on kerättävä kyseisen asian yhteiset ominaisuudet konstruktioon, jolla voidaan määritellä objektin ominaisuuksia - rajapintaan:
rajapinta IThingWithLength {
length: luku;
}
Voimme käyttää IThingWithLength
rajapinta tyypin asia
parametri:
function getLength(thing: IThingWithLength):number {
return thing.length;
}
suoraan sanottuna tässä yksinkertaisessa esimerkissä se on täysin kunnossa, mutta jos haluamme pitää tämän tyypin geneerisenä, emmekä halua kohdata ensimmäisen esimerkin ongelmaa, voimme käyttää tyyppiä Yleiset rajoitukset:
function getLength(thing: T):number {
return thing.length;
}
ja käytä sitä:
rajapinta IBananaPeel {
thickness: number;
length: number;
}
const bananaPeel: IBananaPeel = {thickness: 0.2, length: 3.14};
getLength(bananaPeel);
Käyttämällä laajentaa
varmistaa, että T
sisältää ominaisuuksia, jotka on määritelty IThingWithLength
.
Yleiset luokat
Tähän asti olemme työskennelleet geneeristen funktioiden parissa, mutta se ei ole ainoa paikka, jossa geneeriset funktiot loistavat, joten katsotaanpa, miten voimme sisällyttää ne luokkiin.
Yritetään ensinnäkin säilyttää banaanit banaanikori:
luokka Banaani {
constructor(
public length: number,
public color: string,
public ionizingRadiation: number
) {}
}
class BananaBasket {
private bananas: Banana[] = [];
add(banana: Banana): void { add(banana: Banana): void {
this.bananas.push(banaani);
}
}
const bananaBasket = new BananaBasket();
bananaBasket.add(new Banana(3.14, 'red', 10e-7));
Yritetään nyt luoda yleiskori, joka on tarkoitettu eri tavaroille, joilla on sama tyyppi:
class Basket {
private stuff: T[] = [];
add(asia: T): void {
this.stuff.push(thing);
}
}
const bananaBasket = new Basket();
Ja lopuksi oletetaan, että korimme on radioaktiivisten aineiden säiliö ja että voimme varastoida vain sellaista ainetta, jossa on radioaktiivisia aineita. ionisoiva säteily
omaisuus:
rajapinta IRadioactive {
ionizingRadiation: number;
}
class RadioactiveContainer {
private stuff: T[] = [];
add(asia: T): void {
this.stuff.push(thing);
}
}
Yleinen käyttöliittymä
Lopuksi yritetään kerätä kaikki tietomme ja rakentaa radioaktiivinen imperiumi myös käyttämällä Generic Interfaces -rajapintoja:
// Määritä konttien yhteiset attribuutit
rajapinta IRadioactive {
ionizingRadiation: number;
}
// Määritellään jotain, joka on radioaktiivinen
interface IBanana extends IRadioactive {
length: number;
color: string;
}
// Määritellään jotain, joka ei ole radioaktiivinen
rajapinta IDog {
weight: number;
}
// Määritellään rajapinta säiliölle, joka voi pitää sisällään vain radioaktiivista tavaraa.
interface IRadioactiveContainer {
add(thing: T): void;
getRadioaktiivisuus():number;
}
// Määritellään luokka, joka toteuttaa radioaktiivisen säiliön rajapinnan
class RadioactiveContainer implements IRadioactiveContainer {
private stuff: T[] = [];
add(asia: T): void {
this.stuff.push(thing);
}
getRadioactiveness(): number {
return this.stuff.reduce((a, b) => a + b.ionizingRadiation, 0)
}
}
// ERROR! Tyyppi 'IDog' ei täytä rajoitusta 'IRadioactive'.
// Ja on aika brutaalia säilyttää koiria radioaktiivisen säiliön sisällä.
const dogsContainer = new RadioactiveContainer();
// All good fam!
const radioactiveContainer = new RadioactiveContainer();
// Muista lajitella radioaktiiviset jätteet - luo erillinen säiliö vain banaaneille.
const bananasContainer = new RadioactiveContainer();
Siinä kaikki!