Polimorfni tip povratne vrijednosti u TypeScriptu
Recimo da želimo implementirati hijerarhiju tipova podataka, koji tip bi trebao biti korišten?
interface Collection<T> {
map(f: (v: T) => T): ???
}
interface Array<T> extends Collection<T> {
map(f: (v: T) => T): ??? { return new Array<T>() }
}
interface LinkedList<T> extends Collection<T> {
map(f: (v: T) => T): ??? { return new LinkedList<T>() }
}
Polimorfiziranje this ne radi
interface Collection<T> {
map(f: (v: T) => T): this
}
interface Array<T> extends Collection<T> {
map(f: (v: T) => T): this { return new Array<T> }
// ^ error `Error: type Array<T> is not assignable to type this`
}
Odgovor #1
Iskrena upotreba polimorfnog this je kada zapravo vraćate this. Razmotrite sljedeće klase
export interface Collection<T> {
map(f: (v: T) => T): this
}
class Array<T> implements Collection<T> {
map(f: (v: T) => T): this { return new Array<T>() as this; }
}
class MyArray<T> extends Array<T>{ }
new MyArray().map(o=> true) instanceof MyArray // false, but map returns MyArray
Ništa ne prisiljava MyArray da prepiše map i vrati ispravnu instancu, tako da kada pozovemo map na MyArray i dalje dobivamo instancu Array (možda donekle iznenađujuće, rekao bih).
To rečeno, ne postoji dobar način da se ovo modelira u sustavu tipova, to je klasa koja, kada se naslijedi, mora implementirati određenu metodu bez da klasa bude apstraktna.
Možete koristiti polimorfni this i koristiti svojstvo constructor kako biste se zaštitili od gore navedenog scenarija, ali ne postoji način da se ograniči izvedeni konstruktor da nema parametre (kao što bi poziv u nastavku zahtijevao). Dakle, ovo rješenje nije potpuno tip sigurnosno, i koristi određenu vrstu tvrde provjere tipa da bi posao bio obavljen, ali mislim da je to koliko je sigurno moguće:
interface Collection<T> {
map(f: (v: T) => T): this
}
class Array<T> implements Collection<T> {
map(f: (v: T) => T): this { return new (this.constructor as any)(); }
}
class MyArray<T> extends Array<T>{ }
console.log(new MyArray().map(o=> true) instanceof MyArray) // ture
Odgovor #2
Ako dobro razumijem, ono što tražite je f-ograničena polimorfija („preopterećenje this“).
interface Collection<T> {
map(this: Collection<T>, f: (v: T) => T): this;
}
Tip this uvijek se koristi kao prvi parametar. Primijetite da možemo dodati više preopterećenja. Na primjer, za pretvaranje Collection<number> u Collection<string>, mogli bismo napraviti:
interface Collection<T> {
map(this: Collection<T>, f: (v: T) => T): this;
map<U>(this: Collection<T>, f: (v: T) => U): Collection<U>;
}
Upotreba :
declare const collection: Collection<number>;
collection.map(x => x.toString())
