最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - TypeScript Array.prototype.map declaration - Stack Overflow

programmeradmin1浏览0评论

Spec

According to the MDN specification for Array.prototype.map() map should be used like this...

var new_array = arr.map(callback[, thisArg])

Problem

TypeScript has several overloaded declarations for map, and this makes it very difficult to extend Array<T>.

I would expect to see this (which is in lib.d.ts)...

map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];

But lib.d.ts also has these...

map<U>(this: [T, T, T, T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U, U, U, U];

map<U>(this: [T, T, T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U, U, U];

map<U>(this: [T, T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U, U];

map<U>(this: [T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U];

Objection

Since JavaScript does not allow method overloading, and neither does TypeScript for class implementation, I don't think that TypeScript should allow this for ambient declarations either.

Questions

  1. Why does TypeScript allow overloaded signatures for ambient declarations?
  2. How do I override the map implementation in a class that extends Array?

I've raised this on GitHub too...

Note

ReadonlyArray<T> only has a single signature for map, which is...

 map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];

Spec

According to the MDN specification for Array.prototype.map() map should be used like this...

var new_array = arr.map(callback[, thisArg])

Problem

TypeScript has several overloaded declarations for map, and this makes it very difficult to extend Array<T>.

I would expect to see this (which is in lib.d.ts)...

map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];

But lib.d.ts also has these...

map<U>(this: [T, T, T, T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U, U, U, U];

map<U>(this: [T, T, T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U, U, U];

map<U>(this: [T, T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U, U];

map<U>(this: [T, T], callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): [U, U];

Objection

Since JavaScript does not allow method overloading, and neither does TypeScript for class implementation, I don't think that TypeScript should allow this for ambient declarations either.

Questions

  1. Why does TypeScript allow overloaded signatures for ambient declarations?
  2. How do I override the map implementation in a class that extends Array?

I've raised this on GitHub too... https://github./Microsoft/TypeScript/issues/13785

Note

ReadonlyArray<T> only has a single signature for map, which is...

 map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];
Share Improve this question edited Jan 31, 2017 at 14:21 Matthew Layton asked Jan 31, 2017 at 13:50 Matthew LaytonMatthew Layton 42.5k58 gold badges209 silver badges338 bronze badges 2
  • It's interesting that you didn't ask about what IMO would be the most important aspect here: purpose. As in "why define all these overload signatures in the first place?" -- And the answer to that would be the fact, that TypeScript has a type-concept of arrays with a finite amount of elements, such as [number, number, number] being a 3-element number[]. This is assignable both to and from number[], but is not precisely the same from the piler's point of view. Also, these are also called tuple-types in some context, and can define not-necessarily inter-assignable elem types. – John Weisz Commented Jan 31, 2017 at 15:00
  • @JohnWeisz I get that concept of n-element arrays, but then why does this concept in this respect stop at 5-element arrays? Surely n could be infinite? – Matthew Layton Commented Jan 31, 2017 at 16:08
Add a ment  | 

2 Answers 2

Reset to default 2

(1) If it wouldn't be allowed to overload signatures in ambient declarations how would you get the different signatures in the native js functions/methods?
There are a lot of overloads in the lib.d.ts which reflects how the native js objects work.

(2) You need to tell the piler that you're covering all of the possible declared signatures.
In your case you can do something like:

class A<T> extends Array<T> {
    map<U>(this: Array<U>, ...args: any[]);
    map<U>(this: Array<T>, callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[] {
        return [];
    }
}

The first overload signature takes care of the ones you don't want to bother with.

Your question touches different aspects of TypeScript. I'll handle them individually and then put them all together.

Array<T>

Interfaces serve a dual purpose in TypeScript:

  1. They allow you to create "true" interfaces for other (not yet existing) classes to implement. When you implement it, you have to implement it entiriely, since the interface provides a guarantee of all its members being available.
  2. They allow you to define the interface of an already existent javascript type. This is very useful when you want to use one of the many, many existent libraries and still have the advantages of static typing. These interfaces are usually defined in a .d.ts file (and lib.d.ts file contains the basic JavaScript types). These interfaces are tailored to the existing type and they are usually not meant for you to implement. You can though if you want, but you have to implement all its members.

The Array<T> interface is of the second kind and as such it is not meant for you to implement.

this Parameters in Functions

The this: parameter in the function definition is not a real parameter in the sense that you can pass arguments. It allows you specify which type you expect the this value in the function body to be. If you don't specify it, this will be of type any, which is often not very useful.

Function/Method Overloading

In TypeScript, functions and methods are not overloaded in the sense they are overloaded in languages like Java or C#. You cannot implement it more often than once, but you can define alternate signatures to allow static typing for functions that return variant types or use variant parameters. Especially in .d.ts definition files this is useful and often necessary, since existing libraries use the weak typing of JavaScript to return values or expect parameters of different types. That makes your objection false. You need function overloading to acmodate these JavaScript constructs.

Tuples

In a JavaScript array you can assign values of multiple types in each of its slots. In a TypeScript array definition you specify one type, which the plier enforces. To fill the gap, you can define tuples. A type like [number, string, string] translates to a JavaScript array any[] and TypeScript can still enforce static typing.

Summary

The array method overloads you object against in Array<T> introduce a statically typed this parameter in the event the array is an actual [T, T], [T, T, T], [T, T, T, T], [T, T, T, T, T]. It does not imply that the array provides multiple map methods. It is the same method, but overloaded for some specific array types. It is provided in lib.d.ts and as such was not designed to be implemented by you. You can extend the underlying class (which is already there even without the interface), but the overloads don't hurt you (at least not in this case, since they only provide this parameters).

发布评论

评论列表(0)

  1. 暂无评论