javascript - Miksi TypeScript käyttää 'Like'-tyyppejä?
Miksi TypeScriptiltä löytyy tyyppi ja sitten "tyyppiin liittyvä" tyyppi? Esimerkki tästä on Promise<T> ja PromiseLike<T>.
Mitkä ovat näiden kahden tyypin erot? Milloin minun pitäisi käyttää niitä? Miksi tässä tapauksessa ei vain käytettäisi yhtä Promise-tyyppiä?
Vastaus #1
Jos katsot määritelmätiedostoja (otetaan vaikka lib.es6.d.ts), niin se on varsin suoraviivaista.
Esimerkiksi ArrayLike-liitäntä:
interface ArrayLike<T> {
readonly length: number;
readonly [n: number]: T;
}
on rajatumpi kuin Array-versio:
interface Array<T> {
length: number;
toString(): string;
toLocaleString(): string;
push(...items: T[]): number;
pop(): T | undefined;
concat(...items: T[][]): T[];
concat(...items: (T | T[])[]): T[];
join(separator?: string): string;
reverse(): T[];
shift(): T | undefined;
slice(start?: number, end?: number): T[];
sort(compareFn?: (a: T, b: T) => number): this;
splice(start: number, deleteCount?: number): T[];
splice(start: number, deleteCount: number, ...items: T[]): T[];
unshift(...items: T[]): number;
indexOf(searchElement: T, fromIndex?: number): number;
lastIndexOf(searchElement: T, fromIndex?: number): number;
// lots of other methods such as every, forEach, map, etc
[n: number]: T;
}
On hyvä pitää nämä kaksi erillään, koska saatan haluta tällaisen funktion:
function getSize(arr: Array<any>): number {
return arr.length;
}
console.log(getSize([1, 2, 3])); // works
Mutta se ei toimi tämän kanssa:
function fn() {
console.log(getSize(arguments)); // error
}
Se johtaa tähän virheeseen:
Tyyppinen argumentti 'IArguments' ei ole yhdistettävissä tyyppiin 'any[]'.
Ominaisuutta 'push' ei ole tyypissä 'IArguments'.
Mutta molemmat toimivat, jos teen näin:
function getSize(arr: ArrayLike<any>): number {
return arr.length;
}
(lisää ArrayLike-tietoa MDN:ssä)
Sama pätee Promise ja PromiseLike kanssa: jos rakennan kirjastoa, joka ei ota kantaa Promise-toteutuksen suhteen, sen sijaan että tekisin näin:
function doSomething(promise: Promise<any>) { ... }
Teen tämän:
function doSomething(promise: PromiseLike<any>) { ... }
Sitten, vaikka kirjastoni käyttäjä käyttäisi eri toteutusta (bluebird), se toimii silti aivan hyvin.
Jos huomaat, määritelmästä 'Promise' on tämä:
declare var Promise: PromiseConstructor;
Mikä tekee siitä hyvin spesifin, muut toteutukset saattavat omaa erilaisia ominaisuuksia, esimerkiksi erilaisen prototyypin:
interface PromiseConstructor {
readonly prototype: Promise<any>;
...
}
Arvelen, että pääasiallinen syy siihen, miksi meillä on PromiseLike, on se, että useita toteutuksia oli saatavilla ennen kuin natiivi toteutus tuettiin (kuten bluebird, Promises/A+, jQuery ja muita).
Jotta TypeScript voisi toimia koodikantojen kanssa, jotka käyttävät näitä toteutuksia, täytyy olla tyyppi, joka ei ole Promise, muuten olisi paljon ristiriitoja.
