TypeScript not providing function name

2019-04-26 20:40发布

问题:

I have some typescript code, and I'm doing some metaprogramming where I need to be able to access instance.func.name, however TypeScript omits the function name in the compiled JS.

TypeScript:

class ClassName {
    // ... 
    func(): ReturnType {
        // ...
    }
}

Compiled JavaScript:

// ...
ClassName.prototype.func = function () {
    // ... 
};

Desired JavaScript:

ClassName.prototype.func = function func() {
    // ...                          ^^^^
};

Is there a compiler option I'm missing, or a keyword I can use in TypeScript to accomplish this?

回答1:

A solution, once which I will not be marking as accepted, because it doesn't provide the name property, but does work with any other identifier is as follows:

function named(target: any, key: string) {
    target[key].functionName = key;
}

class ClassName {
    // ... 
    @named
    func(): ReturnType {
         // ...
    }
}

Then access instance.func.functionName.



回答2:

It is not possible to use TypeScript decorators because function.name is readonly property.

There is a hacky way:

class ClassName {
    // ... 
    public func = function test() {

    }

    public func2() {

    }
}

let instance = new ClassName();

console.log("RESULT", instance.func['name']);

but it is not exactly what you ask for (i.e. notice the missing prototype in the function declaration).

Edit: TypeScript compiler does not write the function name because there is no handling for SyntaxKind.MethodDeclaration in emitter.ts:

function shouldEmitFunctionName(node: FunctionLikeDeclaration) {
    if (node.kind === SyntaxKind.FunctionExpression) {
        // Emit name if one is present
        return !!node.name;
    }
    if (node.kind === SyntaxKind.FunctionDeclaration) {
        // Emit name if one is present, or emit generated name in down-level case (for export default case)
        return !!node.name || languageVersion < ScriptTarget.ES6;
    }
}

If you want to get your hands dirty, then you can update ./node_modules/typescript/lib/typescript.js file. Just add the last condition:

function shouldEmitFunctionName(node) {
    if (node.kind === 173 /* FunctionExpression */) {
        // Emit name if one is present
        return !!node.name;
    }
    if (node.kind === 213 /* FunctionDeclaration */) {
        // Emit name if one is present, or emit generated name in down-level case (for export default case)
        return !!node.name || languageVersion < 2 /* ES6 */;
    }

    // MODIFIED
    if (node.kind === 143 /* MethodDeclaration */) {                    
        return true;
    }                                                        
}

and run this to test the change:

$ node ./node_modules/typescript/lib/typescript.js hello.ts