35

TypeScript newbie question. In our project, we are using some external JavaScript libraries where we needed to add *.d.ts files. I understand this use case and the reason why we needed to do this.

But, for our own interfaces that we are defining, one of my developers suggested that we define them in *.d.ts files so that we could have access to the interface type without importing it into the modules that need to use it.

For example, we wanted to create an interface for an "error-first callback" function so that we could reuse it in many areas.

So instead of this...

export function helloWorldEventually(callback: (err: Error, result: any) => void) {
  callback(null, 'Hello World');
}

We could define an interface for the error first callback like this...

export interface ErrorFirstCallback {
  (err: Error, result: any): void;
}

And use it like this...

export function helloWorldEventually(callback: ErrorFirstCallback) {
  callback(null, 'Hello World');
}

At first, I just defined the ErrorFirstCallback interface in ErrorFirstCallback.ts, and imported it in order to reference it.

Another developer suggested we put in in a *.d.ts file and then we would not need to import it in order to reference it.

When should interfaces that we are defining be defined in a *.d.ts file vs a *.ts file.

Thanks!

Kevin
  • 2,084
  • 3
  • 24
  • 36
  • afaik. `*.d.ts`-files are primaryly used to annotate plain `*.js` files/libs with types. I'm not sure wether I'd agree to use such a definition-file or wether I'd put this in a `interfaces.ts` that is then referenced in the main ts-file. Or stay verbose/safe and reference the interface-file in every file that needs it. – Thomas Jun 17 '16 at 00:50

3 Answers3

20

Declaration files describe the shape of external JavaScript libraries. For example using jQuery with $ will result in a TypeScript error without declaration files, because $ or JQuery is undefined. Therefor a declaration file creates an interface, so the compiler knows "if this variable is of type JQuery it must have the functions x,y,z"

When creating interfaces for your project, you should put them where ever you like: Within one big interface-file, within an individual file for each interface, or within the file where it may belong to, BUT within a declaration file would be very inconvenient.

Personally i like to have individual files for each module/class/interface. But its just a matter of taste.

The only thing considering creating a declaration file is to give other developers the possibility to use you final JavaScript file(not TypeScript!) in their project.

Mick
  • 8,203
  • 10
  • 44
  • 66
  • 4
    I am going to follow this general rule: if I am creating an interface, then it should just be a normal *.ts file, but if we need to add an interface or typing for an external JS module, then it makes sense to use a declaration file. Thanks for the answers! – Kevin Jun 19 '16 at 14:57
  • There more you work with interfaces, the more you will understand this. Btw. you find definitions files for most Libaries [on GitHub](https://github.com/DefinitelyTyped/DefinitelyTyped). – Mick Jun 20 '16 at 19:22
16

Another developer suggested we put [the interface] in a *.d.ts file and then we would not need to import it in order to reference it.

Using a .d.ts vs. .ts file is unrelated to providing a type declaration in a local/module or global/script scope.

You can export the interface ErrorFirstCallback, so others will have to import it. Or you don't use export/import to make the file a global script. Then, ErrorFirstCallback can be used without an import. It doesn't matter, if the file has a .ts or .d.ts extension in this case.

When should interfaces that we are defining be defined in a *.d.ts file vs a *.ts file.

What does matter is, that .d.ts files are only seen as compiler input and not emitted to your dist/build folder.

As a good rule of thumb, you put the type in a .ts, if you want to provide it as part of an npm package or public typed API to make your life easier with the build step (it will be emitted as compiler output).

You can use .d.ts files for internally used types of your project.

ford04
  • 66,267
  • 20
  • 199
  • 171
  • 2
    Thanks, this clarified a lot of things. - even if you name the file *.d.ts or *.ts, the only thing that matters is whether you have a top level export or not. If you have a export, it would be a module, which needs to be imported. If you do not have an export, it would be considered a global script, available to all modules. that said, is there a reason to use one naming convention vs other - *.d.ts vs *.ts ? – gaurav5430 Nov 18 '20 at 07:49
  • 1
    Yes, exactly. Concerning `d.ts` vs `.ts`: `.d.ts` primarily is the extension used by the compiler to output type declarations, triggered by [`--declaration`](https://www.typescriptlang.org/docs/handbook/compiler-options.html) or `--emitDeclarationOnly`. For own types, you are on the safe side always using the `.ts` extension. There might be rare situations for `.d.ts` - e.g. you develop a library and want to ensure, certain types are kept internally and not emitted, like a global (type) declaration or module augmentation. – ford04 Nov 18 '20 at 08:09
0

Are you using typings? That seems to be the community way to manage third party types. That way you don't have to manage defining other developers' APIs.

As a point of reference the Node environment d.ts appears to just define the (err, res) callbacks throughout the API (https://github.com/typed-typings/env-node/blob/master/6/node.d.ts)

darthtrevino
  • 2,205
  • 1
  • 15
  • 17