Comparing JavaScript to an example DTS
Common CommonJS Patterns
A module using CommonJS patterns uses module.exports to describe the exported values. For example, here is a module which exports a function and a numerical constant:
js
This can be described by the following .d.ts:
ts
The TypeScript playground can show you the .d.ts equivalent for JavaScript code. You can try it yourself here.
The .d.ts syntax intentionally looks like ES Modules syntax.
ES Modules was ratified by TC39 in 2015 as part of ES2015 (ES6), while it has been available via transpilers for a long time, however if you have a JavaScript codebase using ES Modules:
js
This would have the following .d.ts equivalent:
ts
Default Exports
In CommonJS you can export any value as the default export, for example here is a regular expression module:
js
Which can be described by the following .d.ts:
ts
Or a number:
js
ts
One style of exporting in CommonJS is to export a function. Because a function is also an object, then extra fields can be added and are included in the export.
js
Which can be described with:
ts
Note that using export default in your .d.ts files requires esModuleInterop: true to work.
If you can’t have esModuleInterop: true in your project, such as when you’re submitting a PR to Definitely Typed, you’ll have to use the export= syntax instead. This older syntax is harder to use but works everywhere.
Here’s how the above example would have to be written using export=:
ts
See Module: Functions for details of how that works, and the Modules reference page.
Handling Many Consuming Import
There are many ways to import a module in modern consuming code:
ts
Covering all of these cases requires the JavaScript code to actually support all of these patterns. To support many of these patterns, a CommonJS module would need to look something like:
js
Types in Modules
You may want to provide a type for JavaScript code which does not exist
js
This can be described with:
ts
This example is a good case for using generics to provide richer type information:
ts
Now the type of the array propagates into the ArrayMetadata type.
The types which are exported can then be re-used by consumers of the modules using either import or import type in TypeScript code or JSDoc imports.
Namespaces in Module Code
Trying to describe the runtime relationship of JavaScript code can be tricky.
When the ES Module-like syntax doesn’t provide enough tools to describe the exports then you can use namespaces.
For example, you may have complex enough types to describe that you choose to namespace them inside your .d.ts:
ts
To understand how namespaces work in .d.ts files read the .d.ts deep dive.
Optional Global Usage
You can use export as namespace to declare that your module will be available in the global scope in UMD contexts:
ts
Reference Example
To give you an idea of how all these pieces can come together, here is a reference .d.ts to start with when making a new module
ts
Library file layout
The layout of your declaration files should mirror the layout of the library.
A library can consist of multiple modules, such as
These could be imported as
js
Your declaration files should thus be
Testing your types
If you are planning on submitting these changes to DefinitelyTyped for everyone to also use, then we recommend you:
- Create a new folder in
node_modules/@types/[libname]- Create an
index.d.tsin that folder, and copy the example in- See where your usage of the module breaks, and start to fill out the index.d.ts
- When you’re happy, clone DefinitelyTyped/DefinitelyTyped and follow the instructions in the README.
Otherwise
- Create a new file in the root of your source tree:
[libname].d.ts- Add
declare module "[libname]" { }- Add the template inside the braces of the declare module, and see where your usage breaks