简体   繁体   中英

TypeScript: auto-generated dynamic function names

I have some dynamically generated function names in TypeScript. The only way I can use them now is to cast my objects to <any> . Ex.: <any>myInstance.getDataA() . These functions are dynamically generated based on some rules. Based on the same rules I'd like to generate type-definitions for my class es, but I can not make it work.

original.ts

abstract class Original {

    dynamics = ['getData', 'setData'];

    constructor() {
        // I create functions here dynamically
        this.dynamics.forEach((key) => {
            this[key + this.info] = () => null;
        });
    }

    get info() {
        return 'X';
    }
}

my-class.ts

class MyClass extends Original {
    get info() {
        return 'A';
    }
}

my-other-class.ts

class MyOtherClass extends Original {
    get info() {
        return 'B';
    }
}

something.ts

const myInstance = new MyClass();
console.log(myInstance.getDataA()); // TS2339: Property getDataA does not exist on type: 'MyClass'

const myOtherInstance = new MyOtherClass();
console.log(myInstance.getDataB()); // TS2339: Property getDataB does not exist on type: 'MyClass'

I would like to automatically generate a definition file to define these dynamic properties.

Ex.:

my-class.def.ts

 declare interface MyClass {
    getDataA;
    setDataA
 }

 //my-other-class.def.ts
 declare interface MyClass {
    getDataB;
    setDataB
 }

But I can not find a syntax for my definition files to make it work. Pls ask me if I was not clear, and pls help if you have any idea!

Edit for 4.1

Using Template literal types and mapped type 'as' clauses we can now do concatenate strings in the type system and create a class that has these properties created dynamically.

function defineDynamicClass<T extends string[]>(...info: T): {
    new (): {
        [K in T[number] as `get${Capitalize<K>}`]: () => unknown
    } & {
        [K in T[number] as `set${Capitalize<K>}`]: (value: unknown) => void
    } & {
        info: T
    }
} {
    return class {
        get info () {
            return info;
        }
    } as any
}
class MyClass extends defineDynamicClass('A', 'B', 'ABAB') {
}
let s =new MyClass();
s.getA();
s.getABAB();
s.setA("")
s.info;

Playground Link

Before 4.1

The within language approach

There is no way to do this within the type system, since we can't perform string manipulation on string literal types. The closest you can get, without external tools, is to create get/set methods that take a string literal type, that will be of the same as that returned by the getInfo method.

function stringLiteralArray<T extends string>(...v: T[]){ return v;}

abstract class Original {
    get(name: this['info'][number]) {
        return null;
    }

    set(name: this['info'][number], value: any) {
        return null;
    }

    get info() : string[]{
        return [];
    }
}

class MyOtherClass extends Original {
    get info() {
        return stringLiteralArray('A', 'B', 'ABAB');
    }
}
class MyClass extends Original {
    get info() {
        return stringLiteralArray('C', 'D', 'DEDE');
    }
}

let s =new MyClass();
s.get('A') // error 
s.get('C') // ok

While this approach is not 100% what you want, form our previous discussions the aim was to have full code-completion for the methods, and this approach achieves this. You get errors if you pass in the wrong value and you get a completion list for the string:

在此处输入图片说明

The compiler API approach

A second approach would be to create a custom tool that uses the typescript compiler API to parse the ts files, look for classes derived from Original and generates interfaces containing the methods (either in the same file or a different file) , if you are interested in this I can write the code, but it's not trivial, and while the compiler API is stable I don't think the compiler team takes as much care with backward compatibility as they do with the language (in fact this is the exact statement they make in the documentation page).

If you are interested in such a solution, let me know and I can provide it, but I advise against it.

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