just-boris
just-boris

Reputation: 9746

Export class and namespace from the same file

I have a code with the following structure:

export class Component {
  constructor(options: Component.Options) {}
}

export namespace Component {
  export interface Options {
    // some values here...
  }
}

Then it is used like this:

import {Component} from './component'

const options: Component.Options = {};
new Component(options);

The issue happens, when Options definition is growing too much and we would like to extract namespace with interfaces into a separate file:

./component/interfaces.ts

export namespace Component {
  export interface Options {
    // some values here...
  }
}

./component/index.ts

export * from './interfaces';

export class Component {
  constructor(options: Component.Options) {}
}

When this separation happens, the code becomes not valid, Typescript returns an error

 index.ts:3:16 - error TS2702: 'Component' only refers to a type, but is being used as a namespace here.

 const options: Component.Options = {};
                ~~~~~~~~~

Is there a way to fix it without changing the exported names? We cannot do this, because these interfaces are already consumed by customers and we cannot change the names within a minor version bump.

Upvotes: 0

Views: 1178

Answers (1)

Matt McCutchen
Matt McCutchen

Reputation: 30889

The only way to merge definitions is to define them with the same name in the same scope (global, module (including all augmentations), or namespace) to give a single symbol. Once you have two distinct symbols, you cannot merge them by any means, including trying to export them under the same name. For implementation reasons, a class has the additional restriction that namespaces can only merge with it if they are defined later in the same file. So you'll have to define the Component namespace in index.ts and copy individual members from interfaces.ts; unfortunately, there is no way to use a wildcard. For example, you could write in index.ts:

import { Component as InterfacesComponent } from './interfaces';

export class Component {
  constructor(options: Component.Options) {}
}

export namespace Component {
  export import Options = InterfacesComponent.Options;
}

Or you could omit the namespace layer from interfaces.ts and update the import accordingly if you find that less confusing.

Upvotes: 1

Related Questions