Typescript's global types are essential for defining consistent data structures

Would it be possible to create a file in your typescript code that declares universally accessible types?

I enjoy using typescript, but I often come across the need to import types from various parts of my system just to ensure complete type safety. It can be quite frustrating.

Answer №1

Absolutely, achieving this is definitely possible. All the necessary details can be found at this link

The critical piece to pay attention to is shown below:

declare global {
    /*~ This section is for declaring elements that belong in the global scope or enhance
     *~ existing declarations in the global scope
     */
    interface String {
        fancyFormat(opts: StringFormatOptions): string;
    }
}

Answer №2

After discovering that the accepted answer wasn't working as expected, I decided to do some troubleshooting and managed to get it to work for my specific setup. It's possible that there may be a configuration issue that needs to be addressed. If you encounter any issues with this solution, please let me know and I will remove it.

  1. To start, create a definition file in a convenient folder like types/global.d.ts
  2. Next, check your tsconfig.json and add
    "*": ["types/*.d.ts"]
    under paths. You can also directly reference the created file if needed.
  3. Ensure that your global definitions are placed directly into the root of the file, avoiding the use of declare global or similar syntax.

With these steps completed, you should now be able to utilize the types declared in this file (tested with typescript 3.9.6 and 3.7.5).

For example, your global definition file could look like:

// global.d.ts
declare interface Foo {
    bar: string;
    fooBar: string;
}

Here is an example of how your tsconfig should appear:

[...]
"paths": {
    "*": ["types/*.d.ts"]
},
[...]

Answer №3

Although it may be a bit belated, I discovered that you have the freedom to insert a file.d.ts anywhere within your project and it will automatically be recognized.

For instance, in my own project, I needed:

Optional<T> = T | null;

Unsure of where to place this declaration, I created a common.d.ts file in a directory and included the following:

declare type Optional<T> = T | null;

This solution is functioning perfectly without any errors, even without the need for an import statement. It's worth noting that I tested this in vscode, so results may vary in other editors.

(Keep in mind your specific file include/exclude rules, but most projects incorporate all *.ts files)

Answer №4

The solutions provided above can all coexist harmoniously and complement each other effectively. The key lies in the understanding that declaration files (non-modules) need to be somehow accessible to the project, as they do not have exports. Additionally, there exists a syntax that enables any module file (any file utilizing imports/exports, essentially anything within your src folder) to add ambient declarations.

Understanding Ambient Declarations (Non-Modules)

  • These files must be included in the project (e.g., through tsconfig.json under "include")
  • They should not include any import or export statements
  • To import external types, a specific syntax is available
tsconfig.json
{
  "include": ["./src/global.d.ts"],
  // or use wildcard
  "include": ["**/*.d.ts"],
}
src/globals.d.ts
// Global types

type AnyObject<T = any> = Record<string, T>
type AnyFunction = (...args: any[]) => any

// Enhancing existing interfaces via interface merging

interface Window {
  console: AnyObject
}

// Importing external types

declare type BaseContract = import('ethers').BaseContract
declare type _MockContract = import('ethereum-waffle').MockContract
declare type Stub = import('ethereum-waffle').Stub
// Re-defining an existing interface to provide improved typings.
interface MockContract<T extends BaseContract = BaseContract> extends _MockContract {
  mock: {
    [key in keyof T['functions']]: Stub
  }
}

Javascript Modules

  • Modules consist of files with import or export statements,
    essentially every file within your src folder.
  • Any js (module) file can contribute ambient declarations.
src/app.ts
import React from 'react'

export default MyApp(props: AppProps) {
  return <div>Hi</div>
}

// Ambient declarations

declare global {
  interface Window {
    console: AnyObject
  }
}

To prevent confusion among colleagues, place this disclaimer at the beginning of your Ambient Declarations file

////// -----------------------------------------------------------------------------------------------------------------
/*//// -----------------------------------------------------------------------------------------------------------------

This file serves as an “Ambient declarations file”. The defined types here are globally accessible.
For more information, refer to https://stackoverflow.com/a/73389225/985454

Avoid using `import` and `export` directly in this file! It disrupts ambience.
To import external types in an ambient declarations file (this file), follow this format:

*//**
* @example
* declare type React = import('react')
*//*

To incorporate ambient declarations from any file, including non-ambient ones, use this approach:

*//**
* @example
* declare global {
*   interface Window {
*     ethereum: any
*   }
* }
*//*

/*//// -----------------------------------------------------------------------------------------------------------------
////// -----------------------------------------------------------------------------------------------------------------

// Your type definitions go here ...

Answer №5

Make sure to add this additional information to Sebastian Sebald's response:

Remember to include the following code snippet to turn it into a real module.

export {} 

This step will ensure that everything functions correctly.

Take note of how it should look:

declare global {
    /*~ Add any declarations or enhancements to the global namespace here */
    interface String {
        fancyFormat(opts: StringFormatOptions): string;
    }
}
export {}

Answer №6

When working with global variables in TypeScript, you may face two main challenges:

  • Cannot find name 'variable-name', indicating that the variable is not accessible in the global scope.
  • The global variable type being any, which means that the type hasn't been properly imported or defined.

Solution

  1. To address this, start by declaring the type of the global variable:
// global-types.d.ts
import type { Foo } from "package-with-types";

declare global {
  type Bar = Foo;
}
export {};

💡 Remember, a declaration file is not a module by default, so you need to use export {} to make it a module, import types using import type, and then utilize declare global to define globally accessible types.

  1. Next, declare the actual global variable:
// global.d.ts
// eslint-disable-next-line  @typescript-eslint/no-explicit-any
declare const eruda: any;
declare const bar: Bar;

  1. This approach enables access to the global variable with proper type hints throughout the project.

References

Answer №7

It's funny how sometimes the solution to a problem is right in front of us, but we overlook it. I found myself stuck for hours trying to understand why my *.d.ts files weren't being recognized. It turns out that in my tsconfig.json, the include property was set up like this (the default from a project template I used):

{
  ...
  "include": [
    "src/**/*.ts",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue"
  ]
}

However, I had placed my whatever.d.ts file under types/ to keep my src/ directory organized. 🤦‍♂️ Therefore, the fix for me was simple and straightforward:

  1. Add **/*.d.ts or types/*.d.ts to the include property.
  2. Remove the include property entirely and rely on the default settings.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When transitioning to angular 10 and removing decorators from classes, what is the best approach for dealing with a base class that is inherited by both Directives and Injectables?

During the angular 10 migration process, there is a recommendation that classes utilizing angular features should have a decorator. However, what should be done in cases where it's a base class shared by both Injectables and Directives (as it solely i ...

Exploring the potential of Chart.js within an Angular 2 application

Currently, I am delving into the world of Angular2 paired with TypeScript and I have a desire to integrate the Chartjs module into my application. The chartjs documentation has instructions on how to achieve this using the traditional canvas html tag: < ...

Ionic 4 - Best practices for handling asynchronous data with multiple observables

I successfully accessed this route: http://localhost:8100/questions/question?id=3 However, I am facing a challenge in handling two subscribers simultaneously. The first subscriber is responsible for loading the questions array from an external service. ...

List of nested objects converted into a flat array of objects

Looking to transform a data structure from an array of objects containing objects to an objects in array setup using JavaScript/Typescript. Input: [ { "a": "Content A", "b": { "1": "Content ...

What is the best way to retrieve URL parameters in Node.js using TypeScript?

In the onRequest method provided below, I encounter an error: onRequest(request: Http.IncomingMessage, response: Http.ServerResponse): void { response.writeHead(200, {"Content-Type": "text/plain"}); const tc = new TaxCalculator(); ...

Is it necessary for me to set up @types/node? It appears that VSCode comes with it pre-installed

Many individuals have been adding @types/node to their development dependencies. Yet, if you were to open a blank folder in VSCode and create an empty JavaScript file, then input: const fs = require('fs'); // <= hover it and the type display ...

Is it possible to customize a VSCode extension to function exclusively with certain programming languages?

Lately, I added a new extension to my VSCode setup that has proven to be quite beneficial for my workflow. If you're interested, you can find this helpful extension at This Repository. It allows you to easily highlight the starting and ending syntax ...

Troubleshooting: Unable to Trigger Click Events Outside Angular Component

Check out the code I've put together on Stackblitz. Here, you'll find my custom component that allows for toggling between two ng-content elements based on click events. import {Component, ElementRef, EventEmitter, OnDestroy, OnInit ...

Protected class, yet not transferable

My output varies based on the type of input provided. I have a custom guard in place to protect the input, but I'm still having trouble assigning it to the declared output: type InputType<Sub extends SubType> = { a: Sub, b: string } type SubTyp ...

When debugging, the App.tsx file appears blank, even though no errors are showing

Hey there, I'm facing an issue with this code where it's showing up blank without any errors. I'm not sure if I missed a library or misplaced something. The code is for creating reusable components and asking questions in a GPT chat. I have ...

Retrieve the href value from a string and then associate the modified data with the element in an Angular application

Within my innerHtml, I have a string that looks like this: <div class="wrapper" [innerHTML]="data.testString"> The data inside data.testString is as follows, data.testString="<p>some info, email <a href="mailto ...

Achieving selective exclusion of specific keys/values while iterating through an array and rendering them on a table using Angular

Currently facing a hurdle and seeking advice I am developing an angular application that fetches data from an API and presents it on the page The service I am utilizing is named "Api Service" which uses HTTPClient to make API calls apiservice.service.ts ...

What is the best way to showcase a standalone JSON object within the template?

I have a detailed component that is designed to show the 5-day forecast for a specific city. I have successfully retrieved the data using the http.get(Url) method. However, I am unsure of how to bind this JSON data to my view. I am familiar with displayi ...

Trouble creating a declaration file for a single TypeScript module

Here is a question that has been previously asked: How can a library with type definitions be generated? The answers provided suggest simply setting "declaration" to true in the tsconfig.json file. For an example of this concept, I have created both examp ...

Incorporate a personalized add-button into the material-table interface

My current setup includes a basic material-table structured like this: <MaterialTable options={myOptions} title="MyTitle" columns={state.columns} data={state.data} tableRef={tableRef} // Not functioning properly editabl ...

Error in redirection while deploying Auth.js (v5) within a Docker container in a Next.js application

Has anyone successfully integrated the latest version of Auth.js into a production environment with Docker? I am currently utilizing the t3-stack (tRPC, Auth.JS, Prisma, Next.JS). I attempted to upgrade to the beta version with the Prisma Adapter, but enc ...

Converting an object within an object into an Angular Class (Type)

Is there a way to convert the value of obj.category into the Category type as shown in the example below? I specifically need this conversion in order to select options in a dropdown. export class Category{ id: number; name: string; construc ...

React: Leveraging error boundaries to identify malfunctioning components

In my project using React 16.3 and TypeScript, I've been exploring error boundaries with the ComponentDidCatch() function overrides. To improve future debugging processes, I'm interested in catching errors at the component level to visually highl ...

Aggregate the values in an array and organize them into an object based on their frequency

I have an array of information structured like this: 0: { first: "sea", second: "deniz", languageId: "English-Turkish"} 1: { first: "play", second: "oynamak", languageId: "English-Turkish&qu ...

Enhancing Skylinkjs functionality using Typescript

Hello fellow developers, I am new to using typescript and currently experimenting with incorporating SkylinkJS into my project. Can anyone guide me on the best practices for utilizing an npm library with TypeScript? If you are interested in checking out t ...