How to merge namespace has no exported interface in TypeScript

会有一股神秘感。 提交于 2020-05-13 02:38:03

问题


I am using the queue lib Bull in TypeScript. Its definition is:

node_modules/@types/bull/index.d.ts

declare const Bull: {
  (queueName: string, opts?: Bull.QueueOptions): Bull.Queue;
  // something like above
};

declare namespace Bull: {
  interface Queue {}
  interface Job {}

  // some other non-exported interfaces
}

export = Bull

I want to merge the namespace Bull in my library and use it in another app.

node_modules/myLib/index.d.ts

import { Queue } from 'bull'

declare namespace Bull: {
  export interface Queues {}
}

export interface myInterface {
  foo: Queue | Bull.Queues
}

export = Bull

myApp/foo.ts

import { Job, Queues } from 'myLib' // Error, 'myLib' has no exported member 'Job'

According to the doc, namespace is a GLOBAL variable, and namespaces of the same name will merge their EXPORTED interfaces. So, how can I merge the namespace Bull from @types/bull? Thanks!


回答1:


Well, the truth is that @types\bull is not really declaring a namespace.

Well, it is, but just to group a list of related types and export them together as the default export, so, what it really exports are the contents of the namespace, not the namespace itself. That's why you can import Queue, and use Queueand not Bull.Queue, which is what you should have to do if Queue truly belonged to a namespace.

Besides, I don't know what version of TypeScript you're using, but you shouldn't be able to use export (...) and export = (...) in the same file. Besides, when you add export to a file, it turns into a declaration file for a module, so, in the end, you have a module that exports a namespace as default, and then you can import Queues from myLib, but not Job, as Job does not appear anywhere in the file and therefore it is not exported.

To be able to merge namespaces in different files, you can't use imports or exports, just declarations, because two modules can never contribute names to the same namespace. By using export, you're turning your files into modules, and once you do so, namespaces in them do not belong to the global scope anymore, so even if they have the same names, they really belong to the scope of their own module and do not merge.

To do what you're trying to do, you'd have to have:

bull:

declare const Bull: {
    (queueName: string, opts?: any): Bull.Queue;
    // something like above
  };

declare namespace Bull {
    interface Queue {}
    interface Job {}

    // some other non-exported interfaces
}

myLib:

declare namespace Bull {
  export interface Queues {}
}

declare interface myInterface {
  foo: Bull.Queue | Bull.Queues
}

And now you truly have one namespace with the contents of both declarations:

test.ts:

const a: Bull.Queues = {};
const g: Bull.Queue = {};
const b: Bull.Job = {};

This way it would work, but, unfortunately, is not what you have. You should define myLib as:

import * as Bull from './bull';

export interface Queues {};


export interface myInterface {
  foo: Bull.Queue | Queues;
}

and then you can use:

import * as Bull from './bull';
import { Queues, myInterface } from './myLib';

const a: Queues = {};
const g: Bull.Queue = {};
const b: Bull.Job = {};

const instance: myInterface = null;

or, if you prefer,

import * as Bull from './bull';
import * as ExtendedBull from './myLib';

const a: ExtendedBull.Queues = {};
const g: Bull.Queue = {};
const b: Bull.Job = {};

const instance: ExtendedBull.myInterface;

But, in any case, you need to import from both bull and myLib.



来源:https://stackoverflow.com/questions/49724861/how-to-merge-namespace-has-no-exported-interface-in-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!