Why does TypeScript use "Like" types?
If you look at the definition files (let's take lib.es6.d.ts) then it's pretty straight forward.
For example the ArrayLike interface:
interface ArrayLike<T> {
readonly length: number;
readonly [n: number]: T;
}
is more limited than the Array one:
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;
}
It's good to have the two separated because I might want to have a function like this:
function getSize(arr: Array<any>): number {
return arr.length;
}
console.log(getSize([1, 2, 3])); // works
But it won't work with this:
function fn() {
console.log(getSize(arguments)); // error
}
It results with this error:
Argument of type 'IArguments' is not assignable to parameter of type 'any[]'.
Property 'push' is missing in type 'IArguments'.
But both will work if I do this:
function getSize(arr: ArrayLike<any>): number {
return arr.length;
}
(more on ArrayLike in MDN)
The same with Promise
and PromiseLike
, if I'm building a library which isn't opinionated about the implementation of the Promise
then instead of doing this:
function doSomething(promise: Promise<any>) { ... }
I'll do this:
function doSomething(promise: PromiseLike<any>) { ... }
Then even if the user of my library is using a different implementation (bluebird) it will work just fine.
If you'll notice the definition of Promise is this:
declare var Promise: PromiseConstructor;
Which makes it very specific, other implementations might have different properties, for example a different prototype:
interface PromiseConstructor {
readonly prototype: Promise<any>;
...
}
I guess that the main reason that we have PromiseLike
is that several implementations were available before the native one was supported (such as bluebird, Promises/A+, jQuery, and more).
In order for typescript to work with code bases that are using those implementations there must be a type other than Promise
, otherwise there would be a lot of contradictions.