Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambient declaration with an imported type in TypeScript

Tags:

typescript

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?

like image 217
harryg Avatar asked Jul 14 '17 09:07

harryg


People also ask

What is ambient declaration in TypeScript?

Ambient declarations are a way of telling the TypeScript compiler that the actual source code exists elsewhere. When you are consuming a bunch of third party js libraries like jquery/angularjs/nodejs you can't rewrite it in TypeScript.

What is ambient module TypeScript?

Ambient modules is a TypeScript feature that allows importing libraries written in JavaScript and using them seamlessly and safely as if they were written in TypeScript. An ambient declaration file is a file that describes the module's type but doesn't contain its implementation.

How do I write an import statement in TypeScript?

Use import myFunction from "./myModule" to bring it in. More commonly, TypeScript modules say export myFunction in which case myFunction will be one of the properties on the exported object. Use import { myFunction } from "./myModule" to bring it in.


1 Answers

Yes, there is a way. It has become easier in TypeScript 2.9 by using import() a type but it's also possible in earlier versions of TypeScript.

The following file is a script. Things declared in a script are added to the global scope. This is why you can use MyApp.MyThing without importing it.

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

Scripts are limited in that they cannot import anything; when you add an import, the script becomes a module. I think it's weird to say the least, but it is what it is. What matters is that things defined in a module are scoped to that module, and are not added to the global scope.

However, modules can add declarations to the global scope too, by putting them inside global:

// myapp.d.ts import {SomeType} from 'module';  declare global {   namespace MyApp {     interface MyThing {       prop1: string;       prop2: number;       prop3: SomeType;     }   } } 

This file is a module, but it adds a declaration of MyApp.MyThing to the global scope, so you can still use MyApp.MyThing in other TypeScript code without importing it.

Note that using the .d.ts extension has nothing to do with you being able to access the interface without importing it. Both of the above files could have been .ts files and would still behave exactly the same.

like image 87
Roman Starkov Avatar answered Sep 28 '22 22:09

Roman Starkov