Typescript 命名空间——字段的动态定义(JS 到 TS)

TypesScript namespace - dynamic definition of fields (JS to TS)

我正在尝试在不破坏 public 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, or using @jsx etch.dom an example

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

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

这里是my branch.

我假设您正在为 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 以了解相关要求。我将突出显示此摘录:

Intrinsic elements are looked up on the special interface JSX.IntrinsicElements. [...] if this interface is present, then the name of the intrinsic element is looked up as a property on the JSX.IntrinsicElements interface.

综上所述,这是一个有效的适度类型定义:

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
    }
  }
}