Bruce Sun
Bruce Sun

Reputation: 661

What is the correct way to extend TypeScript definitions in a 3rd party library?

I'm using TypeScript 4.5.5

There's an interface DvaInstance in a 3rd party library dva, existing index.d.ts for this library looks sth. like:

export interface DvaInstance {
  // ...
}

export {
  connect, connectAdvanced, useSelector, useDispatch, useStore,
  DispatchProp, shallowEqual
} from 'react-redux';

A field named _store exists in JavaScript but is missing in this interface, and now I want to extend this interface to include this field.

So I went ahead and created a dva.d.ts:

declare module 'dva' {
  interface DvaInstance {
    _store: import('redux').Store;
  }
}

Then this field (_store) became available. However, tsc started to complain when I do import { connect } from 'dva':

Module '"dva"' has no exported member 'connect'.

Looks like my module declaration overrides dva's, and declaration merging is not working as I expected.

I searched online and a lot of articles (including official documentation) use import statement. So I tried again:

import { DvaInstance } from 'dva';

declare module 'dva' {
  interface DvaInstance {
    _store: import('redux').Store;
  }
}

This time, both import { connect } from 'dva' and _store field works, but tsc complains that 'DvaInstance' is already declared in the upper scope.

My questions are:

Upvotes: 5

Views: 3152

Answers (1)

Bruce Sun
Bruce Sun

Reputation: 661

To properly trigger declaration merging, I should use import 'dva' instead of import { xxx } from 'dva'.

dva.d.ts:

import 'dva'; // Trigger declaration merging

declare module 'dva' {
  interface DvaInstance {
    _store: import('redux').Store;
  }
}

However there's still one question unanswered: a top level import or export makes the .d.ts file a non-ambient module, how come the types in it are still available without explicitly importing it in other files (import 'dva.d.ts')?

Upvotes: 4

Related Questions