【问题标题】:TypesScript namespace - dynamic definition of fields (JS to TS)Typescript 命名空间 - 字段的动态定义(JS 到 TS)
【发布时间】:2020-03-12 01:55:08
【问题描述】:

我正在尝试在不破坏公共 API 的情况下将此 JavaScript 代码从 atom/etch 转换为 TypeScript。

它同时定义了一个dom-function 和一个dom-array of functions(都具有相同的名称dom):

// called by the following loop
function dom (tag, props, ...children) {
// ...
}

const HTML_TAGS = [
  'a', 'abbr'] // ... has more elements though
// similarly SVG-Tags is defined

// finds the array of functions
for (const tagName of HTML_TAGS) {
  dom[tagName] = (props, ...children) => {
    return dom(tagName, props, ...children)
  }
}

for (const tagName of SVG_TAGS) {
  dom[tagName] = (props, ...children) => {
    return dom(tagName, props, ...children)
  }
}

module.exports = dom

什么是等效的 TypeScript 版本?

其他包使用 dom 比如dom.a(tag, props, childern)an example,或者使用@jsx etch.doman example

通过运行dts-gen,我得到一个名为dom 的命名空间,其中包含在for 循环中定义的所有函数。

export namespace dom {
    function a(props: any, children: any): any;
    function abbr(props: any, children: any): any;
//...
}

这里是my branch

【问题讨论】:

    标签: javascript typescript metaprogramming


    【解决方案1】:

    我假设您正在为dom.js 创作一个dom.d.ts 类型定义文件。

    命名空间不是dom 的正确类型。运行时 JS 中的 TS 命名空间呈现为普通对象。但是dom 既是一个可调用函数又是一个具有额外属性的对象。因此,您应该使用带有可调用签名的接口来表示 TS 中的dom

    dom.d.ts

    interface EtchElement<T extends string, P = any> {
      tag: T;
      props: P;
      children: any[];
      ambiguous: any[];
    }
    
    type EtchCreateElement<T extends string, P> = (props: P, ...children: any[]) => EtchElement<T, P>;
    
    interface EtchDOM {
      <T extends string, P>(tag: T, props: P, ...children: any[]): EtchElement<T, P>;
      div: EtchCreateElement<"div", any>;
      // ... more tags here
    }
    
    declare const dom: EtchDOM;
    
    export = dom;
    

    JSX 支持

    现在如果你也打算支持 JSX 的使用,你需要先通读官方的JSX guide 来了解需求。我将突出显示这段摘录:

    在特殊接口 JSX.IntrinsicElements 上查找内部元素。 [...] 如果此接口存在,则内部元素的名称将作为 JSX.IntrinsicElements 接口上的属性进行查找。

    综合起来,这是一个可行的适度类型定义:

    interface EtchElement<T extends string, P = any> {
      tag: T;
      props: P;
      children: any[];
      ambiguous: any[];
    }
    
    type EtchCreateElement<T extends string, P> = (props: P, ...children: any[]) => EtchElement<T, P>;
    
    interface EtchDOM {
      <T extends string, P>(tag: T, props: P, ...children: any[]): EtchElement<T, P>;
      div: EtchCreateElement<"div", JSX.IntrinsicElements["div"]>;
      // ... more tags here
    }
    
    declare const dom: EtchDOM;
    
    export = dom;
    
    declare global {
      namespace JSX {
        interface Element extends EtchElement<any, any> {}
        interface IntrinsicElements {
          div: any; // constraint on props of "div" element
        }
      }
    }
    
    

    【讨论】:

    • 感谢您的详细解答!关于您的第一个假设,我正在尝试在 TS 中编写整个内容:github.com/aminya/etch/tree/type_definitions/lib_src
    • @Amin 你可以将我的定义合并到你的.ts 文件中。复制粘贴,然后更改导出:const _dom: EtchDOM = dom as EtchDOM; export default _dom;
    猜你喜欢
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2016-09-28
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    • 2012-10-20
    • 1970-01-01
    相关资源
    最近更新 更多