Exporting additional interfaces for CommonJS module (Typescript)

I am trying to use a simple JS library in Typescript / React, but I cannot create a definition file for it. Library - google-kgsearch ( https://www.npmjs.com/package/google-kgsearch ). It exports one CommonJS style function. I can successfully import and call the function, but cannot figure out how to refer to the type of the arguments on the result callback.

Here's most of the library code:

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

      

And here is my attempt to model it. Most of the interfaces simulate the results returned by the service:

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

      

My problem is that from another file, I cannot reference the KGS.EntitySearchResult array returned by the search callback. Here is my use of the library:

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();
    }
    ....
}

      

Any suggestions on how to make the result interfaces visible to my calling code without messing up the default export is greatly appreciated.

+3


source to share


1 answer


The problem is easily solved here. The problem is that while you were exporting KGSearch

, you weren't exporting the namespace KGS

that contains the types. There are several ways to do this, but I recommend using Ad Merge

Your code will change as follows

declare module 'google-kgsearch' {

    export = KGSearch;

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

      

Then from the consumption code

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

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

      


Unfortunately, whenever we enter an external external module declaration, as we do, write declare module 'google-kgsearch'

in the global scope, we pollute the global external module namespace (which is what I know). While this is unlikely to cause a conflict in your particular project at the moment, it does mean that if someone adds a package @types

for google-kgsearch

, and you have a dependency that in turn depends on that package, @types

or if google-kgsearch

everyone starts submitting their own icons, we we will run into errors.

To solve this problem, we can use a non-surrounding module to declare our custom ads, but this requires a bit more configuration.



Here's how we can do it

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 (name doesn't matter, just needs to match paths)

// 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 {
    // ...
}

      

This encapsulates us from version conflicts and transitive dependency issues by defining them as a plug-in, not an external plug-in.


The last thing to seriously consider is submitting a pull request krismuniz / google-kgsearch , which will add your gripe (second version) to a file called index.d.ts . Also, if the maintainers don't want to include them, consider creating a package @types/google-kgsearch

by submitting a pull request to DefinitelyTyped

+2


source







All Articles