Generics bjóða upp á endurnýtanlega kóðahluta sem virka með mörgum tegundum í stað einnar tegundar. Generics bjóða upp á leið til að meðhöndla tegund sem breytu og tilgreina hana við notkun, líkt og stikluðu breytur í falli.
Almenn gerðir má nota í tengslum við fall (til að búa til almennt fall), flokka (til að búa til almennan flokk) og viðmót (til að búa til almennt viðmót).
Grunnnotkun
Þú hefur líklega notað generics áður án þess að vita af því – algengasta notkun generics er að lýsa fylki:
const myArray: string[];
Það er ekki mjög sérstakt við fyrstu sýn, við erum bara að lýsa. mínaraðir sem fylki strengja, en það er það sama og almenn yfirlýsing:
const myArray: Array;
Halda hlutunum skýrum
Skulum byrja á mjög einföldu dæmi – hvernig gætum við flutt þetta vanilla JS fall til TypeScript:
getPrefiledArray(filler, length) {
return (new Array(length)).fill(filler);
}
Þessi fall mun skila fylki fylltu með gefnu magni af fylliefni, svo lengd mun verða tala og heilli fallið mun skila fylki af fylliefni – en hvað er fylliefni? Á þessum tímapunkti getur það verið hvað sem er, svo einn kostur er að nota hvert sem er:
function getPrefiledArray(filler: any, length: number): any[] {
return (new Array(length)).fill(filler);
}
Að nota hvert sem er er vissulega almenn – við getum borið bókstaflega hvað sem er, svo “að vinna með mörgum gerðum í stað einnar gerðar” úr skilgreiningunni er fullkomlega tekið með, en við missum tengslin milli fylliefni Gerðin og skilargerðin. Í þessu tilfelli viljum við skila einhverju sameiginlegu og getum við skýrt skilgreint þetta sameiginlega sem Gerðartilkynning:
function getPrefiledArray(filler: T, length: number): T[] {
return (new Array(length)).fill(filler);
}
og nota svona:
const prefilledArray = getPrefiledArray(0, 10);
Almenn takmörk
Skoðum mismunandi, líklega algengari tilvik. Af hverju notum við raunverulega gerðir í fallum? Fyrir mig er það til að tryggja að þau rök sem send eru í fallið hafi ákveðna eiginleika sem ég vil vinna með.
Aftur skulum við reyna að flytja einfalt vanilla JS-fall yfir í TS.
getLength(hlutur) {
skila lengd hlutursins;
}
Við stöndum frammi fyrir verulegu vandi – hvernig tryggjum við að hluturinn hafi lengd eign, og fyrsta hugmyndin gæti verið að gera eitthvað eins og:
getLength(thing: typeof Array):number {
return thing.length;
}
og allt eftir samhengi gæti það verið rétt, en almennt erum við dálítið almenn – það virkar með fylkjum af mörgum gerðum, en hvað ef við vitum ekki alveg hvort hluturinn eigi alltaf að vera fylki – kannski er hluturinn fótboltavöllur eða bananahýði? Í þessu tilfelli verðum við að safna sameiginlegum eiginleikum hlutarins í uppbyggingu sem getur skilgreint eiginleika hlutskiptis – viðmót:
interface IThingWithLength {
length: number;
}
Við getum notað Ég-hlutur-með-lengd viðmót sem tegund af the hlutur breyti:
getLength(thing: IThingWithLength):number {
return thing.length;
}
Alveg hreint sagt í þessu einföldu dæmi verður þetta alveg í lagi, en ef við viljum halda þessari tegund almennri og forðast vandamálið úr fyrsta dæminu getum við notað Almenn takmörk:
function getLength(thing: T):number {
return thing.length;
}
og nota það:
interface IBananaPeel {
thickness: number;
length: number;
}
const bananaPeel: IBananaPeel = {thickness: 0.2, length: 3.14};
getLength(bananaPeel);
Að nota útvíkkar tryggir að T mun innihalda eiginleika sem eru skilgreindir af Ég-hlutur-með-lengd.
Almennar flokkar
Hingað til höfum við unnið með generískum fallum, en þetta er ekki einasta sviðið þar sem generíkar skara fram úr. Skoðum hvernig við getum innleitt þær í bekki.
Fyrst skulum við reyna að geyma knippi af banana í bananakörfunni:
class Banana {
constructor(
public length: number,
public color: string,
public ionizingRadiation: number
) {}
}
class BananaBasket {
private bananas: Banana[] = [];
add(banana: Banana): void {
this.bananas.push(banana);
}
}
const bananaBasket = new BananaBasket();
bananaBasket.add(new Banana(3.14, ‘rauður’, 10e-7));
Nú skulum við reyna að búa til alhliða körfu fyrir mismunandi hluti af sama tagi:
class Basket {
private stuff: T[] = [];
add(thing: T): void {
this.stuff.push(thing);
}
}
const bananaBasket = new Basket();
Og að lokum skulum við gera ráð fyrir að körfan okkar sé geislavirk efniílát og við getum aðeins geymt efni sem hefur jónandi geislun Eign:
interface IRadioactive {
ionizingRadiation: number;
}
class RadioactiveContainer {
private stuff: T[] = [];
add(thing: T): void {
this.stuff.push(thing);
}
}
Almenn viðmótsaðgerð
Að lokum skulum við reyna að safna allri okkar þekkingu og byggja upp geislavirk heimsveldi með því að nota Generic Interfaces:
// Skilgreina sameiginlega eiginleika fyrir ílát
interface IRadioactive {
ionizingRadiation: number;
}
// Skilgreina eitthvað sem er geislavirk
interface IBanana extends IRadioactive {
length: number;
color: string;
}
// Skilgreina eitthvað sem er ekki geislavirk
interface IDog {
weight: number;
}
// Skilgreina viðmót fyrir ílát sem getur aðeins geymt geislavirk efni
interface IRadioactiveContainer {
add(thing: T): void;
getRadioactiveness():number;
}
// Skilgreina bekk sem innleiðir viðmót fyrir geislavirk ílát
class RadioactiveContainer implements IRadioactiveContainer {
private stuff: T[] = [];
add(thing: T): void {
this.stuff.push(thing);
}
getRadioactiveness(): number {
return this.stuff.reduce((a, b) => a + b.ionizingRadiation, 0)
}
}
// ERROR! Type 'IDog' does not satisfy the constraint 'IRadioactive'
// Og það er frekar grimmilegt að geyma hunda í geislavirkum íláti
const dogsContainer = new RadioactiveContainer();
// Allt gott, fjölskylda!
const radioactiveContainer = new RadioactiveContainer();
// Mundu að flokka geislavirk úrgang - búðu til sérstakt tunnu fyrir bananar einir
const bananasContainer = new RadioactiveContainer();
Þetta er allt, fólk!