为CommonJS模块(Typescript)导出额外的接口

7
我想在Typescript/React中使用一个简单的JS库,但是无法为它创建定义文件。该库是google-kgsearch(https://www.npmjs.com/package/google-kgsearch),以CommonJS风格导出单个函数。我可以成功地导入和调用该函数,但是无法确定如何引用结果回调的参数类型。
以下是大部分库代码:
function KGSearch (api_key) {
  this.search = (opts, callback) => {
    ....
    request({ url: api_url, json: true }, (err, res, data) => {
      if (err) callback(err)
      callback(null, data.itemListElement)
    })
    ....
    return this
  }
}

module.exports = (api_key) => {
  if (!api_key || typeof api_key !== 'string') {
    throw Error(`[kgsearch] missing 'api_key' {string} argument`)
  }

  return new KGSearch(api_key)
}

以下是我对其建模的尝试。大多数接口都会建模服务返回的结果:

declare module 'google-kgsearch' {

    function KGSearch(api: string): KGS.KGS;
    export = KGSearch;

    namespace KGS {

        export interface SearchOptions {
            query: string,
            types?: Array<string>,
            languages?: Array<string>,
            limit?: number,
            maxDescChars?: number
        }

        export interface EntitySearchResult {
            "@type": string,
            result: Result,
            resultScore: number
        }

        export interface Result {
            "@id": string,
            name: string,
            "@type": Array<string>,
            image: Image,
            detailedDescription: DetailedDescription,
            url: string
        }

        export interface Image {
            contentUrl: string,
            url: string
        }

        export interface DetailedDescription {
            articleBody: string,
            url: string,
            license: string
        }

        export interface KGS {
            search: (opts: SearchOptions, callback: (err: string, items: Array<EntitySearchResult>) => void) => KGS.KGS;
        }
    }
}

我的问题是,我无法从另一个文件中引用搜索回调返回的KGS.EntitySearchResult数组。以下是我的库使用方式:
import KGSearch = require('google-kgsearch');
const kGraph = KGSearch(API_KEY);

interface State {
    value: string;
    results: Array<KGS.EntitySearchResult>; // <-- Does not work!!
}

class GKGQuery extends React.Component<Props, object> {    

    state : State;

    handleSubmit(event: React.FormEvent<HTMLFormElement>) {
        kGraph.search({ query: this.state.value }, (err, items) => { this.setState({results: items}); });
        event.preventDefault();
    }
    ....
}

非常感谢您对如何使结果接口对我的调用代码可见的任何建议,但请不要搞乱默认导出。

使用 import ... = require('...'); 来引入 CommonJS 模块,这是一个非常好的实践。赞! - Aluan Haddad
1个回答

4
这里的问题很容易解决。问题在于,您已经导出了KGSearch,但是未导出包含类型的命名空间KGS。有几种方法可以解决这个问题,但我建议利用声明合并来解决。
您的代码将更改如下:
declare module 'google-kgsearch' {

    export = KGSearch;

    function KGSearch(api: string): KGSearch.KGS;
    namespace KGSearch {
        // no changes.
    }
}

然后从消费代码开始

import KGSearch = require('google-kgsearch');
const kGraph = KGSearch(API_KEY);

interface State {
    value: string;
    results: Array<KGSearch.EntitySearchResult>; // works!!
}

很遗憾,每当我们引入一个环境外部模块声明时,就像我们在全局范围内编写declare module 'google-kgsearch'一样,我们会污染环境外部模块的全局命名空间(我知道这很啰嗦)。虽然它不太可能在您特定的项目中引起冲突,但这意味着如果有人为google-kgsearch添加了一个@types包,并且您有一个依赖项反过来又依赖于此@types包,或者如果google-kgsearch开始提供自己的类型,则会遇到错误。

要解决这个问题,我们可以使用非环境模块来声明我们的自定义声明,但这需要更多的配置。

以下是如何进行操作:

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "." // if not already set
    "paths": { // if you already have this just add the entry below
      "google-kgsearch": [
        "custom-declarations/google-kgsearch"
      ]
    }
  }
}

custom-declarations/google-kgsearch.d.ts(名称不重要,只需要与路径匹配)


该文件是Google知识图谱搜索API的声明文件。
// do not put anything else in this file

// note that there is no `declare module 'x' wrapper`
export = KGSearch;

declare function KGSearch(api: string): KGSearch.KGS;
declare namespace KGSearch {
    // ...
}

将其定义为外部模块而不是环境外部模块,可以使我们在版本冲突和传递依赖问题方面更加封装。


最后要认真考虑的一件事是向 krismuniz/google-kgsearch 发送一个拉取请求,将您的类型定义(第二个版本)添加到名为 index.d.ts 的文件中。此外,如果维护者不希望包含它们,请考虑通过向 DefinitelyTyped 发送拉取请求来创建一个 @types/google-kgsearch 包。


2
谢谢,这是一个很好的答案。我没有意识到通过使用相同的名称,命名空间会被合并到导出函数中,但它确实有效。我也会尝试您的其他配置建议。 - Brian
是的,这是必要的原因与CommonJS模块和UMD样式声明有关。否则,您可以单独导出它们。好处是,这使得消费者的导入更加清晰。 - Aluan Haddad
2
一旦我更多地使用它们并且对于API的结果确实与我的定义相匹配更有信心,我将通过PR发送类型。再次感谢 :) - Brian
太棒了,越多越好。很高兴我能帮上忙。 - Aluan Haddad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接