javascript - Varför använder TypeScript 'Like'-typer?


Varför har TypeScript en typ och sedan en "liknande typ"? Ett exempel på detta är Promise<T> och PromiseLike<T>.

Vilka är skillnaderna mellan dessa två typer? När ska jag använda dem? I det här fallet, varför inte bara ha en Promise-typ?

Svar #1

Om du tittar på definitionsfilerna (låt oss ta lib.es6.d.ts) så är det ganska enkelt.

Till exempel gränssnittet ArrayLike:

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

är mer begränsad än Array-versionen:

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

Det är bra att ha de två separerade eftersom jag kanske vill ha en funktion som denna:

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

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

Men det kommer inte att fungera med detta:

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

Det resulterar i detta fel:

Argument av typen 'IArguments' kan inte tilldelas parametern av typen 'any[]'.
Egenskapen 'push' saknas i typen 'IArguments'.

Men båda kommer att fungera om jag gör så här:

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

(mer om ArrayLike på MDN)

Samma sak med Promise och PromiseLike, om jag bygger ett bibliotek som inte har några åsikter om implementationen av Promise så istället för att göra detta:

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

Jag ska göra detta:

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

Då kommer det att fungera alldeles utmärkt även om användaren av mitt bibliotek använder en annan implementation (bluebird).

Om du märker är definitionen av löfte denna:

declare var Promise: PromiseConstructor;

Vilket gör det mycket specifikt, andra implementationer kan ha olika egenskaper, till exempel en annan prototyp:

interface PromiseConstructor {
    readonly prototype: Promise<any>;

    ...
}

Jag antar att huvudorsaken till att vi har PromiseLike är att flera implementationer fanns tillgängliga innan den inbyggda stöddes (såsom bluebird, Promises/A+, jQuery och fler).
För att TypeScript ska fungera med kodbaser som använder dessa implementationer måste det finnas en typ annan än Promise, annars skulle det bli många motsägelser.

Relaterade artiklar