简体   繁体   中英

Get filename of derived class from base class in typescript running on node.js?

I'm looking for a way to get the filename of a derived class from a base class in typescript running on node.js. An example of this would be:

Foo.ts

export abstract class Foo {
    constructor() { }
    name() { return (__filename); }
    print() { console.log(this.name()); }
}

Bar.ts

import { Foo } from './Foo';

export class Bar extends Foo {
    constructor() { super(); }
}

main.ts

import { Bar } from './Bar';

let bar = new Bar();
bar.print(); // should yield the location of Bar.ts

Due to the number of files involved and just cleanliness I'd like this to be confined to the Foo class rather than having an override of the name() function in each derived class.

I was able to sort-of solve this with the code:

private getDerivedFilePath(): string {
    let errorStack: string[] = new Error().stack.split('\n');
    let ret: string = __filename;
    let baseClass: any = ThreadPoolThreadBase;
    for (let i: number = 3; i < errorStack.length; i++) {
        let filename: string = errorStack[i].slice(
            errorStack[i].lastIndexOf('(') + 1,
            Math.max(errorStack[i].lastIndexOf('.js'), errorStack[i].lastIndexOf('.ts')) + 3
        );
        let other: any = require(filename);
        if (other.__proto__ === baseClass) {
            ret = filename;
            baseClass = other;
        } else {
            break;
        }
    }
    return (ret || '');
}

Added to Foo , which will work when called from the constructor to set a private _filename property, for inheritance chains beyond the example above so long as the files are structured with a default export of the class being used. There may also be a caveat that if a base class from which a derived object is inheriting directly is initialized as a separate instance within the constructor of any member of the inheritance chain it could get confused and jump to another independent derived class - so it's a bit of a hacky work-around and I'd be interested if someone comes up with something better, but wanted to post this in case someone stumbles across this question and it works for them.

You can use require.cache to get all cached NodeModule objects and filter it to find your module.

https://nodejs.org/api/modules.html#requirecache

class ClassA {
    public static getFilePath():string{
        const nodeModule = this.getNodeModule();
        return (nodeModule) ? nodeModule.filename : "";
    }
    
    public static getNodeModule(): NodeModule | undefined{
        const nodeModule = Object.values(require.cache)
            .filter((chl) => chl?.children.includes(module))
            .filter((mn)=> mn?.filename.includes(this.name))
            .shift();
        return nodeModule;
    }
}
class ClassB extends ClassA {
    constructor(){}
}

const pathA = ClassA.getFilePath(); //Must return the absolute path of ClassA
const pathB = ClassB.getFilePath(); //Must return the absolute path of ClassB

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM