microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.3k stars 12.39k forks source link

lib.es*.d.ts: constructor methods #44883

Open mfulton26 opened 3 years ago

mfulton26 commented 3 years ago

lib Update Request

Configuration Check

My compilation target is ES2015 and my lib is the default.

Missing / Incorrect Definition

Array and other interfaces for built-ins do not declare their own constructor methods

Sample Code

[].constructor[Symbol.species];
Element implicitly has an 'any' type because expression of type 'unique symbol' can't be used to index type 'Function'.
  Property '[SymbolConstructor.species]' does not exist on type 'Function'.

The code compiles fine when defining the appropriate constructor method:

declare global {
  interface Array<T> {
    constructor: ArrayConstructor;
  }
}

Playground link

Documentation Link

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor

RyanCavanaugh commented 3 years ago

Why would you do it this way instead of Array[Symbol.species], though?

mfulton26 commented 3 years ago

e.g. a custom map function or any function that receives an array and wants to honor the Symbol.species] property of the incoming array to return an array of possibly the same type

declare function map<T, R>(array: T[], mapper: (value: T) => R): R[];

here you wouldn't want to statically reference Array but dynamically get the constructor for the incoming array

mfulton26 commented 3 years ago

and here's a more concrete, non-array example: creating a map function for Set (same could go for Map)

export default function map<T, R>(
  this: Set<T>,
  mapper: (value: T) => R
): Set<R> {
  const result = new (<SetConstructor>this.constructor)[Symbol.species]<R>();
  for (const value of this) {
    result.add(mapper(value));
  }
  return result;
}

I think the cast shouldn't be needed but currently is