How to automatically apply argument to class constructor?
You can't do it directly. If you control the target function (ie it's not the stock map
function) you can have it take a constructor instead of the function:
class User { constructor(private id: number) { }}
function map<TIn, T>(value: TIn, ctor: new (a: TIn) => T): T{
return new ctor(value)
}
map(10, User)
Another more flexible solution is to use a helper function that transform the constructor into the desired function, although it's not much shorter then the original version:
class User { constructor(private id: number) { }}
function ctor<TIn, T>(ctor: new (a: TIn) => T): (value: TIn) => T{
return value => new ctor(value)
}
[10, 11].map(ctor(User));
You could add a check with new.target
if the function is called without new and call then function with new
.
function Person(name) {
if (!new.target) return new Person(...arguments);
this.name = name;
}
var names = ['Jane', 'Dan', 'Grace', 'Paul'],
instances = names.map(Person);
console.log(instances);
You can create a static function in your class that takes the value param and returns a new User:
class User {
static createUser(value) {
return new User(value)
}
}
And then use:
map(User.createUser)
The pattern you are describing is called a scope-safe constructor. It can be implemented by overloading a constructor so it works with and without the new
keyword.
interface User {
name: string;
}
interface UserConstructor {
new (name: string): User;
(name: string): User;
}
The same trick is used for global objects like Array
or Date
.
We need to recognize whether the new
keyword was used:
const User = function (this: User | void, name: string): User {
if (!(this instanceof User)) {
return new User(name);
}
this.name = name;
return this;
} as UserConstructor;
Your class has just become new
-agnostic.
console.log(
new User('Bob'),
User('Alice'),
);
Which enabled us to write:
['Alice', 'Bob'].map(User); // $ExpectType User[]