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.
source to share
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
source to share