Ambient declaration with an imported type in TypeScript

Viewed 13431

I have a declaration file in my TypeScript project like so:

// myapp.d.ts
declare namespace MyApp {
  interface MyThing {
    prop1: string
    prop2: number
  }
}

This works great and I can use this namespace anywhere in my project without having to import it.

I now need to import a type from a 3rd party module and use it in my ambient declaration:

// myapp.d.ts
import {SomeType} from 'module'

declare namespace MyApp {
  interface MyThing {
    prop1: string
    prop2: number
    prop3: SomeType
  }
}

The compiler now complains that it can't find namespace 'MyApp', presumably because the import prevents it from being ambient.

Is there some easy way to retain the ambient-ness of the declaration whilst utilising 3rd-party types?

5 Answers

Since TS 2.9 this is possible with import():

// myapp.d.ts
declare type SomeType = import('module').SomeType;
declare type SomeDefaultType = import('module-with-default-export').default;


declare namespace MyApp {
  interface MyThing {
    prop1: string;
    prop2: number;
    prop3: SomeType | SomeDefaultType;
  }
}

Don't know if you're still looking for an answer, but this is the correct way to handle it and still be able to e.g. define generic modules, not just named namespaces: (original answer this is based on)

// myapp.d.ts

declare namespace MyApp {
  import {SomeType} from 'module'
  interface MyThing {
    prop1: string
    prop2: number
    prop3: SomeType
  }
}

In my case, I have a global ambient variable created by electron for the API to interact with the node process. I needed to define an interface with a property type that referenced a class from a third-party dependency rather than a type/interface.

The TS 2.9 import() syntax worked for me when using a type/interface, but not a class. Specifically I am trying to use rsjx.Observable in my interface. I was able to find a way to handle this that I haven't seen mentioned anywhere. I hope it can help someone else.

You can combine module + ambient by making a module with types you want to export, then use the TS 2.9 import() syntax to pull that into an ambient declaration.

// types/electronAPI.ts
import { IpcRendererEvent } from 'electron'
import { Observable } from 'rxjs'

export type ThingStatus = 'on' | 'off'

export interface MyElectronAPI {
  thingObservable: Observable<[IpcRendererEvent, ThingStatus]>
}
// types/global.d.ts
declare type MyElectronAPI = import('./electronAPI').MyElectronAPI

const electronAPI: MyElectronAPI
// someFile.tsx

// I get all my types :)
elecronAPI.thingObservable.subscribe(([_e, status]) => { ... } )
Related