28

This one's probably extremely simple but I'm going nuts trying to figure it out.

I have a very simple self-executing function in an outer index.ts file that needs to register a global variable on the window object. I want to assign it like so:

(() => {
  window.myCustomGlobal = new MyCustomGlobal();
})()

I created index.d.ts alongside index.ts:

import { MyCustomGlobal} from './classes';

declare interface Window {
  myCustomGlobal: MyCustomGlobal;
}

No errors in the above, all looks good. The application compiles fine.

In the IDE, however, I get an error: ts2339: Property 'myCustomGlobal' does not exist on type 'Window'

I can't for the life of me figure out what exactly makes Typescript "figure out" what .d.ts file to include. In certain libs, having a .d.ts file alongside a .js file causes it to get picked up, and it works fine. In fact, I used the exact same interface as above with a global third-party lib and it worked great; it just seems like my IDE won't pick it up.

I've tried adding index.d.ts to tsconfig.json in include as well as files with no luck.

Can somebody explain, as if to a child, what causes TS to pick up declaration files? Some days I feel like I get it, and then in stupid simple examples like this I feel like an idiot.

Thanks

dudewad
  • 13,215
  • 6
  • 37
  • 46

4 Answers4

44

There might be two issues here:

1.) You need to augment Window interface in global scope.

Either remove all import/export keywords to make the containing file a non-module/script. Or you can use declare global in a module to mark everything wrapped in it as global declaration:

import { MyCustomGlobal} from './classes';

declare global {
  interface Window {
    myCustomGlobal: MyCustomGlobal;
  }
}

2.) Equally named files, that only differ in their .ts/.d.ts extension, don't work. To quote the docs:

Please note that the compiler does not include files that can be possible outputs; e.g. if the input includes index.ts, then index.d.ts and index.js are excluded. In general, having files that differ only in extension next to each other is not recommended.

Make sure to give a different name to index.d.ts (like global.d.ts), so the compiler doesn't only pick index.ts. global.d.ts will included automatically, if placed somewhere in the TS project marked by tsconfig.json and if "files" and "include" config options are left unspecified.

ford04
  • 66,267
  • 20
  • 199
  • 171
  • 10
    That was it -- it was the file name. What a way to waste a couple hours trying to figure this out. I had tried the declare global syntax and it wasn't working, but only because of the file name. Thanks a lot!! – dudewad Jan 14 '20 at 16:48
  • 1
    but what I can do if I want to write d.ts file by hand to some .js file? – pery mimon Jul 23 '20 at 18:04
  • @perymimon Not sure, what you mean. Can you give an example? The precedence order for types is `.ts > .d.ts > .js`. So, given equally named `index.d.ts` and `index.js` file, types are taken from former as desired. – ford04 Jul 24 '20 at 11:00
  • I try to write `.d.ts` file for `.js` file that already exist. and I want vscode or typescript recognize the definition so when I hover over the function I will see the definition as in the `d.ts` file – pery mimon Jul 24 '20 at 11:14
  • @perymimon [Module resolution](https://www.typescriptlang.org/docs/handbook/module-resolution.html) should resolve that case automatically, if the `.d.ts` file has the same path/name as the `.js` one. This resolution works even in the project, if you don't have a `tsconfig.json` (just tried it). Above behavior is different for global types (not `import`ed files) - in this case, tsconfig.json exclude rules also play a role. I suggest to open a new question, as this seems to be non-standard behavior. – ford04 Jul 24 '20 at 11:37
  • I encountered similar issue but still doesn't work. ;_: And adding `declare global {...}` will cause `Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.ts(2669) ` which noted in https://stackoverflow.com/questions/57132428/augmentations-for-the-global-scope-can-only-be-directly-nested-in-external-modul – kuanyui Jun 23 '21 at 03:27
  • @kuanyui you only need `declare global` in a module (file with `import`/`export`). For a script file you can simply omit this. – ford04 Jun 24 '21 at 05:59
  • "So make sure to rename index.d.ts to something like global.d.ts" is what fixed it for me! – Bryant Nov 18 '21 at 15:10
  • Btw: the docs link content has changed and does not include quoted passage anymore. I'll leave it here anyway, as it's imo still a precise description. – ford04 Nov 24 '21 at 16:35
  • Part 2 was absolutely the ticket, thank you – light24bulbs Jan 23 '22 at 18:58
  • The second part was helpful. I named my definition file `index.d.ts` and spent one-hour checking if my config was correct. You saved my day! – SiegeSailor Mar 04 '22 at 16:57
  • I added the `.d.ts` to tsconfig's `"files"`, and now it is included globally, even if I have a `.ts` file with the same name. – Fayez Nazzal Jan 04 '23 at 03:59
19

We declared "include": ["src"] in our tsconfig.json and this caused declarations.d.ts in root ("/") to not be detected.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
xRiot
  • 239
  • 2
  • 2
2

This is just so weird, but, In order for it to work I needed to include the types folder in the include secion in the tsconfig.json

shamaseen
  • 2,193
  • 2
  • 21
  • 35
-1

On my end. I have to add DOM under lib in tsconfig.json so typescript compiler recognized window object.

The.Wolfgang.Grimmer
  • 1,172
  • 2
  • 9
  • 32