【问题标题】:TypeScript: auto-generated dynamic function namesTypeScript:自动生成的动态函数名称
【发布时间】:2018-12-04 04:22:35
【问题描述】:

我在 TypeScript 中有一些动态生成的函数名称。我现在可以使用它们的唯一方法是将我的对象转换为<any>。例如:<any>myInstance.getDataA()。这些函数是根据一些规则动态生成的。基于相同的规则,我想为我的classes 生成类型定义,但我无法让它工作。

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'

我想自动生成一个定义文件来定义这些动态属性。

例如:

my-class.def.ts

 declare interface MyClass {
    getDataA;
    setDataA
 }

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

但是我找不到我的定义文件的语法来使它工作。如果我不清楚,请问我,如果您有任何想法,请提供帮助!

【问题讨论】:

  • 还有更多的属性吗?还是只有一个?因为只有一个人不值得麻烦..
  • 有很多。我为示例保持简单
  • 最大的问题是我们不能在映射类型中进行字符串操作,所以如果getInfo返回一个字符串字面量类型A,我们就不能创建一个名为getDataA的属性
  • 它确实有效,我有 getDataA 函数,但如果我想调用它,我必须将我的实例转换为 any
  • 我相信如果你转换为 any 它会起作用,我是说你不能在类型系统中执行这个突变

标签: javascript typescript types


【解决方案1】:

为 4.1 编辑

使用Template literal types and mapped type 'as' clauses,我们现在可以在类型系统中连接字符串并创建一个动态创建这些属性的类。

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

4.1之前

内部语言方法

在类型系统中无法做到这一点,因为我们无法对字符串字面量类型执行字符串操作。在没有外部工具的情况下,最接近的方法是创建采用字符串文字类型的 get/set 方法,该方法与 getInfo 方法返回的相同。

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

虽然这种方法不是 100% 你想要的,但从我们之前的讨论来看,我们的目标是对方法进行完整的代码完成,而这种方法实现了这一点。如果传入错误的值并获得字符串的完成列表,则会出现错误:

编译器API方法

第二种方法是创建一个自定义工具,使用 typescript compiler API 解析 ts 文件,查找派生自 Original 的类并生成包含方法的接口(在同一文件或不同文件中) (这是他们在文档页面中所做的确切声明)。

如果您对这样的解决方案感兴趣,请告诉我,我可以提供,但我不建议这样做。

【讨论】:

  • 我们在最新的 TS 版本中有模板字面量类型,现在这里没有更优雅的解决方案吗?
  • @ZoltanMagyar 感谢您引起我的注意。是的,我们现在可以做得更好
  • 感谢您的更新。你确定新的例子是正确的吗?我在操场 TS2344 上收到以下错误:类型“K”不满足约束“字符串”。类型“T”不能分配给类型“字符串”。类型 'string[]' 不能分配给类型 'string'。
  • @ZoltanMagyar Wops 在最后一刻做出了改变,并没有注意到我打破了这个例子。现在工作
  • 现在好像没问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-08-14
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 2015-09-11
相关资源
最近更新 更多