liammclennan
liammclennan

Reputation: 5368

How can a type be specified for a class with the 'name' property?

I would like to define a function having an argument that is a constructor. Within the function I want to access the name property of the constructor. Is it possible to create this type constraint with TypeScript?

function doSomethingWithAConstructor(ctor: { new (); name: string }): string {
    return ctor.name;
}

update:

Expected usage is:

class A {}

doSomethingWithAConstructor(A);

Upvotes: 0

Views: 57

Answers (2)

user799755
user799755

Reputation:

Functions have names in ES6 and will be implemented in some JavaScript environments, as noted here on MDN. Yet in TypeScript, Function.name does not exist because it targets ES3 or ES5. But TypeScript allows you to extend interfaces. So the first thing you might want to do is add this to the top of your file:

interface Function { name: string; }

The next thing to note is that there is nothing special about a constructor, it is just a function. Therefore, the correct way to write your function is:

function doSomethingWithAConstructor(ctor: Function): string {
    return ctor.name;
}

If you'd like to protect yourself against environments that haven't implemented Function.name, and/or you're willing to define constructors by the convention they are the only functions that start with a capital letter, add this as the first line of your function:

function doSomethingWithAConstructor(ctor: Function): string {
    if (!ctor || !ctor.name || ctor.name[0] === ctor.name[0].toLowerCase())
        throw 'Please specify a constructor';

    return ctor.name;
}

Finally, there's a library called Classical.js* that makes this much easier. First of all, the Function interface extension is not required. Secondly, ES6 is not required - the name will be parsed from the function definition if it is not defined. Here's what the code would look like:

function doSomethingWithAConstructor(ctor: Function): string {
    return typeOf(ctor).name;
}

Or perhaps even

class A {}
typeOf(A).name; //'A'

If you'd like to try it, visit the Classical.js link, download the code, open up bin and add classical.js and classical.d.ts to your project.

Hope that helps.


* I am one of the developers on the classical team

Upvotes: 1

Ryan Cavanaugh
Ryan Cavanaugh

Reputation: 221064

The code posted is specific to a zero-argument constructor, which is why it rejects classes whose constructors have more than one argument. You can use ... to indicate an arbitrary number of parameters.

The name property on functions is specific to ES6 and thus not part of the TypeScript definition for Function. Note that IE11 does not support this property yet, so do any of the following at your own risk of compatibility problems. You can add it to the Function global type if you want:

interface Function {
    name: string;
}

function doSomethingWithAConstructor(ctor: { new (...args: any[]): any; }): string {
    return ctor.name;
}

Or you can use a type assertion in the function body:

function doSomethingWithAConstructor(ctor: { new (...args: any[]): any; }): string {
    var c = <{name: string;}><any>ctor;
    return c.name;
}

Upvotes: 0

Related Questions