Τα generics παρέχουν επαναχρησιμοποιήσιμα κομμάτια κώδικα που λειτουργούν με έναν αριθμό τύπων αντί για έναν τύπο. Τα generics παρέχουν έναν τρόπο να αντιμετωπίζετε τον τύπο ως μεταβλητή και να τον προσδιορίζετε κατά τη χρήση, παρόμοια με τις παραμέτρους συναρτήσεων.
Οι γενικές μπορούν να χρησιμοποιηθούν σε συνδυασμό με συναρτήσεις (δημιουργία γενικής συνάρτησης), κλάσεις (δημιουργία γενικής κλάσης) και διεπαφές (δημιουργία γενικής διεπαφής).
Βασική χρήση
Πιθανόν να έχετε χρησιμοποιήσει generics στο παρελθόν ακόμα και χωρίς να το γνωρίζετε - η πιο συνηθισμένη χρήση generics είναι η δήλωση ενός πίνακα:
const myArray: string[],
Δεν είναι πολύ ιδιαίτερο με την πρώτη ματιά, απλά δηλώνουμε myArray
ως πίνακας συμβολοσειρών, αλλά είναι το ίδιο με μια γενική δήλωση:
const myArray: Array,
Κρατώντας τα πράγματα ρητά
Ας ξεκινήσουμε με ένα πολύ απλό παράδειγμα - πώς θα μπορούσαμε να μεταφέρουμε αυτή τη λειτουργία JS σε TypeScript:
function getPrefiledArray(filler, length) {
return (new Array(length)).fill(filler),
}
Αυτή η συνάρτηση θα επιστρέψει πίνακα γεμάτο με τη δεδομένη ποσότητα των πληρωτικό
, οπότε μήκος
θα είναι αριθμός
και ολόκληρη η συνάρτηση θα επιστρέψει πίνακα πληρωτικό
- αλλά τι είναι το υλικό πλήρωσης; Σε αυτό το σημείο μπορεί να είναι οτιδήποτε, οπότε μια επιλογή είναι να χρησιμοποιήσετε οποιοδήποτε
:
function getPrefiledArray(filler: any, length: number): any[] {
return (new Array(length)).fill(filler),
}
Χρήση του οποιοδήποτε
είναι σίγουρα γενική - μπορούμε να περάσουμε κυριολεκτικά οτιδήποτε, οπότε η "εργασία με έναν αριθμό τύπων αντί για έναν τύπο" από τον ορισμό καλύπτεται πλήρως, αλλά χάνουμε τη σύνδεση μεταξύ των πληρωτικό
τύπο και τον τύπο επιστροφής. Σε αυτή την περίπτωση, θέλουμε να επιστρέψουμε κάποιο κοινό πράγμα, και μπορούμε να ορίσουμε ρητά αυτό το κοινό πράγμα ως παράμετρος τύπου:
function getPrefiledArray(filler: T, length: number): T[] {
return (new Array(length)).fill(filler),
}
και χρησιμοποιήστε το ως εξής:
const prefilledArray = getPrefiledArray(0, 10),
Γενικοί περιορισμοί
Ας δούμε διαφορετικές, πιθανώς πιο συνηθισμένες περιπτώσεις. Γιατί χρησιμοποιούμε τύπους σε συναρτήσεις; Για μένα, είναι για να διασφαλίσω ότι τα ορίσματα που θα περάσουν στη συνάρτηση θα έχουν κάποιες ιδιότητες με τις οποίες θέλω να αλληλεπιδράσω.
Για άλλη μια φορά ας προσπαθήσουμε να μεταφέρουμε μια απλή συνάρτηση JS vanilla στο TS.
function getLength(thing) {
return thing.length,
}
Έχουμε ένα μη τετριμμένο αίνιγμα - πώς να εξασφαλίσουμε ότι το πράγμα έχει μια μήκος
ιδιότητα, και η πρώτη σκέψη μπορεί να είναι να κάνουμε κάτι σαν:
function getLength(thing: typeof Array):number {
return thing.length,
}
και ανάλογα με το πλαίσιο μπορεί να είναι σωστό, συνολικά είμαστε λίγο γενικοί - θα λειτουργήσει με πίνακες πολλαπλών τύπων, αλλά τι γίνεται αν δεν ξέρουμε πραγματικά αν το πράγμα πρέπει να είναι πάντα ένας πίνακας - ίσως το πράγμα είναι ένα γήπεδο ποδοσφαίρου ή μια μπανανόφλουδα; Σε αυτή την περίπτωση, πρέπει να συγκεντρώσουμε τις κοινές ιδιότητες αυτού του πράγματος σε μια κατασκευή που μπορεί να ορίσει ιδιότητες ενός αντικειμένου - μια διεπαφή:
διεπαφή IThingWithLength {
length: αριθμός,
}
Μπορούμε να χρησιμοποιήσουμε IThingWithLength
διεπαφή ως τύπο της πράγμα
παράμετρος:
function getLength(thing: IThingWithLength):number {
return thing.length,
}
ειλικρινά σε αυτό το απλό παράδειγμα, θα είναι απολύτως εντάξει, αλλά αν θέλουμε να κρατήσουμε αυτόν τον τύπο γενικό, και να μην αντιμετωπίσουμε το πρόβλημα από το πρώτο παράδειγμα μπορούμε να χρησιμοποιήσουμε το Γενικοί περιορισμοί:
function getLength(thing: T):number {
return thing.length,
}
και να το χρησιμοποιήσετε:
διεπαφή IBananaPeel {
thickness: number,
length: number,
}
const bananaPeel: IBananaPeel = {thickness: 0.2, length: 3.14},
getLength(bananaPeel),
Χρήση του επεκτείνει το
εξασφαλίζει ότι T
θα περιέχει ιδιότητες που ορίζονται από IThingWithLength
.
Γενικές κλάσεις
Μέχρι αυτό το σημείο, δουλεύαμε με γενικές συναρτήσεις, αλλά δεν είναι το μόνο μέρος όπου οι γενικές λάμπουν, ας δούμε πώς μπορούμε να τις ενσωματώσουμε σε κλάσεις.
Πρώτα απ' όλα, ας προσπαθήσουμε να αποθηκεύσουμε ένα μάτσο μπανάνες στο καλάθι με τις μπανάνες:
κλάση 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, 'red', 10e-7)),
Τώρα ας προσπαθήσουμε να δημιουργήσουμε καλάθι γενικής χρήσης, για διαφορετικά πράγματα με τον ίδιο τύπο:
class Basket {
private stuff: T[] = [];
add(thing: T): void {
this.stuff.push(thing),
}
}
const bananaBasket = new Basket(),
Και τέλος, ας υποθέσουμε ότι το καλάθι μας είναι ένα δοχείο ραδιενεργών υλικών και ότι μπορούμε να αποθηκεύσουμε μόνο ύλη που έχει ιονίζουσα ακτινοβολία
ιδιοκτησία:
διασύνδεση IRadioactive {
ionizingRadiation: number,
}
class RadioactiveContainer {
private stuff: T[] = [];
add(thing: T): void {
this.stuff.push(thing),
}
}
Γενική διεπαφή
Τέλος, ας προσπαθήσουμε να συγκεντρώσουμε όλες τις γνώσεις μας και να χτίσουμε μια ραδιενεργή αυτοκρατορία χρησιμοποιώντας επίσης Generic Interfaces:
// Καθορισμός κοινών χαρακτηριστικών για τα δοχεία
interface IRadioactive {
ionizingRadiation: number,
}
// Ορίζουμε κάτι που είναι ραδιενεργό
interface IBanana extends IRadioactive {
length: number,
color: string,
}
// Ορίστε κάτι που δεν είναι ραδιενεργό
interface IDog {
weight: number,
}
// Ορισμός διεπαφής για δοχείο που μπορεί να περιέχει μόνο ραδιενεργά πράγματα
interface IRadioactiveContainer {
add(thing: T): void,
getRadioactiveness():number,
}
// Ορισμός κλάσης που υλοποιεί τη διεπαφή radioactive container
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! Ο τύπος 'IDog' δεν ικανοποιεί τον περιορισμό 'IRadioactive'
// Και είναι κάπως βάναυσο να αποθηκεύουμε σκύλους μέσα σε ραδιενεργό δοχείο
const dogsContainer = new RadioactiveContainer(),
// All good fam!
const radioactiveContainer = new RadioactiveContainer(),
// Θυμηθείτε να ταξινομήσετε τα ραδιενεργά σας απόβλητα - δημιουργήστε ξεχωριστό κάδο μόνο για τις μπανάνες
const bananasContainer = new RadioactiveContainer(),
Αυτό είναι όλο, παιδιά!