【问题标题】:Can I instantiate TypeScript classes via proxy?我可以通过代理实例化 TypeScript 类吗?
【发布时间】:2017-11-18 06:34:02
【问题描述】:

注意:我已经更新了这个问题,希望能让事情更清楚。

我正在尝试通过名为elemTypes 的代理对象来实例化类。这个对象是一个类的引用列表,我想使用这些类来以编程方式构建基于类似 DOM 的树结构的对象。我对 TypeScript 很陌生,所以我希望我只是在这里遗漏了一些简单的东西?

代码如下:

type elemDef = {
    type: string,
    name: string,
    childNodes: object[]
}

class Elem {
    childNodes: object[]
    constructor (childNodes: object[]) {
        this.childNodes = childNodes
    }
}

class Div extends Elem {
    constructor (childNodes: object[]) {
        super(childNodes)
    }
}

class Form extends Elem {
    constructor (childNodes: object[]) {
        super(childNodes)
    }
}

class Input extends Elem {
    name: string
    constructor (childNodes: object[], nodeDef: elemDef) {
        super(childNodes)
        this.name = nodeDef.name
    }
}

const div = {
    type: 'Div',
    childNodes: [
        {
            type: 'Form',
            childNodes: [
                {
                    type: 'Input',
                    name: 'username',
                    childNodes: []
                },
                {
                    type: 'Input',
                    name: 'password',
                    childNodes: []
                },
            ]
        }
    ]
}

// I expect that I am doing this part wrong:
const elemTypes = {
    Div,
    Form,
    Input
}

const makeElem = (nodeDef: elemDef) => {
    const childNodes:object[] = []

    nodeDef.childNodes.forEach((childNodeDef: object): void => {
        const element = new elemTypes[childNodeDef.type](childNodeDef)
        childNodes.push(element)
    })

    const element = new elemTypes[nodeDef.type](nodeDef)

    return element
}

// This line just demonstrates that the code is working:
console.log(JSON.stringify(makeElem(div), null, 2))

上面一行的输出是:

{
  "childNodes": {
    "type": "Div",
    "childNodes": [
      {
        "type": "Form",
        "childNodes": [
          {
            "type": "Input",
            "name": "username",
            "childNodes": []
          },
          {
            "type": "Input",
            "name": "password",
            "childNodes": []
          }
        ]
      }
    ]
  }
}

我遇到的问题是 SublimeText 3 给了我这个错误:

Element implicity has a type 'any' type because the type
'{ Div: typeof Div; Form: typeof Form; Input: typeof Input };
 has no index signature;

我已经尝试通过阅读 TypeScript 文档并查看一些 StackOverflow 对类似问题的答案来解决这个问题,但我似乎无法找出我在这里做错了什么。

但是有谁知道是否有一种方法可以定义meta 对象,从而在我的IDE 中停止出现这种隐含的任何错误? (注意:我不想在我的 .ts-config.js 文件中关闭隐式错误。)

我不想关闭 ts-config 中的任何隐含规则。

任何想法表示赞赏。

【问题讨论】:

  • 不应该是person: new Being
  • 你用的是什么IDE?
  • @realharry SublimeText 3

标签: typescript types sublimetext3 implicit index-signature


【解决方案1】:

它在 javascript 中工作,所以它必须在a more convoluted 方式 em>) Typescript 也是如此。


如果“我不知道你想要达到什么目标,但这是你可以做到的!” 看起来是一个可以接受的答案开始,请继续阅读。

在解决主要问题之前进行以下所有更改。

//Be specific. Change all the "object" type annotations to "elemDef". Doing 
//so, the compiler will be able to validate if the type of an argument you are    
//trying to pass to your constructors contains all the properties existent 
//in an "elemDef" object.

type elemDef = {
    type: string;
    name?: string; //<- not all elements have a name, make it optional
    childNodes: elemDef[]; //<- you have a recursive structure.
}

class Elem { 
    readonly type: string;//<- the type of an element is not supposed to change
    childNodes: elemDef[];
    constructor (childNodes: elemDef[]) {
        this.childNodes = childNodes;
        this.type = "Elem";
    }
}


class Div extends Elem {
    readonly type: string;
    constructor (childNodes: elemDef[]) {
        super(childNodes);
        this.type = "Div";
    }
}

class Form extends Elem {
    readonly type: string;
    constructor (childNodes: elemDef[]) {
        super(childNodes);
        this.type = "Form";
    }
}

Input构造函数中参数nodeDef的类型必须是elemDef & { name : string } 所以它不会接受任何参数没有名称的elemDef。

class Input extends Elem {
    readonly type: string;
    name: string;
    constructor (childNodes: elemDef[], nodeDef: elemDef & { name : string }) {
        super(childNodes);
        this.name = nodeDef.name;
        this.type = "Input";
    }
}

那么,还缺少什么?

这是一个对象的索引签名:

{ [key: type1]: type2 }

编译器推断 elemTypes 具有类型:

const elemTypes: {
    Div: typeof Div;
    Form: typeof Form;
    Input: typeof Input;
}

所以有必要提供一个从字符串映射到函数的签名,但是当你这样做时,编译器会告诉你可以使用 new 运算符来调用一个函数缺少构造函数签名。从第二个问题开始:

//this is a type that defines a signature for a generic constructor for elements
type ElementConstructor<T> = {
    new(childNodes: elemDef[]): T;
}

现在我们提供一个签名并使用 ElementConstructor 类型来转换 const elemTypes 中的每个类:

const elemTypes : { [index: string]: ElementConstructor<Elem> } = {
    Div: Div as ElementConstructor<Div>,
    Form: Form as ElementConstructor<Form>,
    Input: Input as ElementConstructor<Input>
};

最后

只需在 makeElement 函数中进行一些调整:

// In your original snippets, you were passing objects to constructor in 
// the forEach but all your constructors take arrays as arguments 
const makeElem = (nodeDef: elemDef) => {
    const childNodes: elemDef[] = [];

    nodeDef.childNodes.forEach((childNodeDef: elemDef): void => {
        const element = new elemTypes[childNodeDef.type]([childNodeDef]); // <- put inside an array
        childNodes.push(element);
    });

    const element = new elemTypes[nodeDef.type]([nodeDef]);  //<- put inside an array
    return element;
}

【讨论】:

  • 太棒了!感谢您抽出宝贵时间对此进行调查。该解决方案似乎需要做很多额外的工作才能使事情变得简单。也许这正是使用 TypeScript 锁定事物所需要的。也许这是因为我的方法是错误的?有没有更好的方法来做我正在做的事情而不需要我构建一个 ElementConturctor 类型?再次感谢您的精彩反馈。非常感谢您花时间向我展示如何进行设置。荣誉@betadeveloper
  • 这只是一个输出预期结果的hacky东西,肯定必须存在更好的方法。事实上,如果您在原始示例中不更改任何内容并尝试在 外部 函数 - new elemTypes["Div"] ([...])-您会看到它有效(!?)。此外,typescript 模块包括所有 html 元素的所有接口的定义......也许你不需要自己实现任何
【解决方案2】:

不完全确定,但也许你可以试试这样的方法,看看是否有帮助?

const proxy: {person: Being} = {
    person: Being
}

【讨论】:

  • 感谢您的建议,我试过了,但没有成功。我已经更新了这个问题,也许这会让事情变得更清楚?
猜你喜欢
  • 2014-09-24
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-21
相关资源
最近更新 更多