javascript - Prečo TypeScript používa typy „Like“?


Prečo má TypeScript typ a potom "ako typ"? Príkladom toho je Promise<T> a PromiseLike<T>.

Aké sú rozdiely medzi týmito dvoma typmi? Kedy by som ich mal použiť? V tomto prípade prečo nepoužiť len jeden typ Promise?

Odpoveď č. 1

Ak sa pozriete na definčné súbory (vezmime si lib.es6.d.ts), potom je to celkom jednoduché.

Napríklad rozhranie ArrayLike:

interface ArrayLike<T> {
    readonly length: number;
    readonly [n: number]: T;
}

je obmedzenejší ako ten v poli:

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;
}

Je dobré mať tieto dve oddelené, pretože môžem chcieť mať funkciu takúto:

function getSize(arr: Array<any>): number {
    return arr.length;
}

console.log(getSize([1, 2, 3])); // works

Ale to s týmto nebude fungovať:

function fn() {
    console.log(getSize(arguments)); // error
}

Vyústí to do tejto chyby:

Argument typu 'IArguments' nie je priraditeľný k parametru typu 'any[]'.
V type 'IArguments' chýba vlastnosť 'push'.

Ale obidva budú fungovať, ak urobím toto:

function getSize(arr: ArrayLike<any>): number {
    return arr.length;
}

(viac o ArrayLike na MDN)

To isté platí pre Promise a PromiseLike, ak budujem knižnicu, ktorá nemá konkrétny názor na implementáciu Promise, potom namiesto toho, aby som robil toto:

function doSomething(promise: Promise<any>) { ... }

Urobím toto:

function doSomething(promise: PromiseLike<any>) { ... }

Takže aj keď používateľ mojej knižnice používa inú implementáciu (bluebird), bude to fungovať úplne v poriadku.

Ak si všimnete, definícia slova 'sľub' je takáto:

declare var Promise: PromiseConstructor;

Čo ho robí veľmi špecifickým, iné implementácie môžu mať odlišné vlastnosti, napríklad iný prototyp:

interface PromiseConstructor {
    readonly prototype: Promise<any>;

    ...
}

Myslím, že hlavným dôvodom, prečo máme PromiseLike, je ten, že pred podporou natívnej implementácie bolo k dispozícii niekoľko implementácií (ako bluebird, Promises/A+, jQuery a ďalšie).
Aby TypeScript mohol pracovať s kódovými základmi, ktoré používajú tieto implementácie, musí existovať typ iný ako Promise, inak by nastalo veľa rozporov.

Súvisiace články