Reputation: 1448
Is there a way to make a file in your typescript file that defines globally accessible types?
I like typescript but find that when I want to be truly type-safe I have to explicitly import types from all over the system. It's rather annoying.
Upvotes: 81
Views: 113023
Reputation: 345
When using global variables in TypeScript, two problems are often encountered:
Cannot find name 'variable-name'
, which means that the variable has not been properly exposed to the global scope.any
, which means that the type has not been properly imported.Solution
// global-types.d.ts
import type { Foo } from "package-with-types";
declare global {
type Bar = Foo;
}
export {};
💡 A declaration file is not a module by default, so you cannot
import
types in a declaration file. Therefore, useexport {}
to make the declaration file a module, import types withimport type
, and then usedeclare global
to define globally accessible types.
// global.d.ts
// eslint-disable-next-line @typescript-eslint/no-explicit-any
declare const eruda: any;
declare const bar: Bar;
References
Upvotes: 2
Reputation: 577
For a lot of people the detail I got myself tripped up on might be obvious. I spent 2 hours trying to figure out why my *.d.ts
files were not getting picked up, and it turns out it was simply because in my tsconfig.json
I had an include
property like this (it was the default that came with a project template I started with):
{
...
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
]
}
But to keep my src/
directory clean I had put my whatever.d.ts
file was under types/
. 🤦♂️ So in my case the fix was simply one of the following:
**/*.d.ts
or types/*.d.ts
or something like that to my include
property.include
property entirely and let it use the default.Upvotes: 1
Reputation: 31919
All the answers above can actually coexist and work together. The key is understanding that declaration files (non-modules) must be "somehow" discoverable by the project, since they don't contain exports. And that there is a syntax that allows any module file (any file that uses imports/exports, basically anything in your src folder) to contribute ambient declarations as well.
tsconfig.json
"include"
)import
or export
statementstsconfig.json
{
"include": ["./src/global.d.ts"],
// or wildcard
"include": ["**/*.d.ts"],
}
src/globals.d.ts
// Global types
type AnyObject<T = any> = Record<string, T>
type AnyFunction = (...args: any[]) => any
// Contributing to existing interfaces (interface merging)
interface Window {
console: AnyObject
}
// Importing
declare type BaseContract = import('ethers').BaseContract
declare type _MockContract = import('ethereum-waffle').MockContract
declare type Stub = import('ethereum-waffle').Stub
// Here we are re-defining an existing interface to provide better typings.
interface MockContract<T extends BaseContract = BaseContract> extends _MockContract {
mock: {
[key in keyof T['functions']]: Stub
}
}
import
or export
statements,src
folder.src/app.ts
import React from 'react'
export default MyApp(props: AppProps) {
return <div>Hi</div>
}
// Ambient declarations
declare global {
interface Window {
console: AnyObject
}
}
Put this at the top of your Ambient Declarations file to avoid coworker confusion
////// -----------------------------------------------------------------------------------------------------------------
/*//// -----------------------------------------------------------------------------------------------------------------
This file is an "Ambient declarations file". The types defined here are available globally.
More info here: https://stackoverflow.com/a/73389225/985454
Don't use `import` and `export` in this file directly! It breaks ambience.
To import external types in an ambient declarations file (this file) use the following:
*//**
* @example
* declare type React = import('react')
*//*
To contribute ambient declarations from any file, even non-ambient ones, use this:
*//**
* @example
* declare global {
* interface Window {
* ethereum: any
* }
* }
*//*
/*//// -----------------------------------------------------------------------------------------------------------------
////// -----------------------------------------------------------------------------------------------------------------
// Your type definitions here ...
Upvotes: 20
Reputation: 397
In addition to Sebastian Sebald's Answer
don't forget to
export {}
which makes it actual module.
so like this.
this is working.
declare global {
/*~ Here, declare things that go in the global namespace, or augment
*~ existing declarations in the global namespace
*/
interface String {
fancyFormat(opts: StringFormatOptions): string;
}
}
export {}
Upvotes: 18
Reputation: 4112
I found the accepted answer is not working (maybe it is some configuration that needs to be done?). So with some tinkering, I got it to work for me (maybe I also have some weird configuration option? Let me know if it doesn't work and I will remove this answer).
types/global.d.ts
tsconfig.json
and include "*": ["types/*.d.ts"]
under paths
. (You should also be able to directly address the created file if you care to).declare global
or similar.Now you should be good to go to use the types declared in this file (tested with typescript 3.9.6 and 3.7.5).
Example file:
// global.d.ts
declare interface Foo {
bar: string;
fooBar: string;
}
What your tsconfig
should look like:
[...]
"paths": {
"*": ["types/*.d.ts"]
},
[...]
Upvotes: 56
Reputation: 825
A little late but, you can add a file.d.ts anywhere in your project as I've noticed, and it will be picked up.
For example, in my project I wanted a:
Optional<T> = T | null;
And I didn't know where to add it, so I added a common.d.ts in a folder, and added:
declare type Optional<T> = T | null;
Now it's being picked up and no errors. Didn't even need to import it. This of course being tested in vscode, not sure if it will work in your editor.
(Depending on your file include/exclude rules of course, but most projects include all *.ts)
Upvotes: 30
Reputation: 16846
Yes this is possible. You can find all information here: https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html
The important part is this:
declare global {
/*~ Here, declare things that go in the global namespace, or augment
*~ existing declarations in the global namespace
*/
interface String {
fancyFormat(opts: StringFormatOptions): string;
}
}
Upvotes: 71