Is there a way to prompt TypeScript to report an error when a mapped key is missing?

Here is my current code snippet:

type TransferType = 'INTERNAL' | 'WITHDRAWAL' | 'DEPOSIT'

type TransferEvents = Record<TransferType, Record<string, TypeFoo | TypeBar>>

export interface EventsTooltip extends TransferEvents {
  some: string;
  extra: number;
  keys: boolean:
  INTERNAL: Record<string, TypeFoo>;
  WITHDRAWAL: Record<string, TypeBar>; 
  DEPOSIT: Record<string, TypeBar>;
}

How can I ensure that TypeScript throws an error if I introduce a new type to TransferType like 'CORRECTION', but forget to include it in the EventsTooltip? Additionally, I want to maintain the ability to be more specific about the value types (TypeFoo or TypeBar) within the records.

Answer №1

There are a couple of approaches that come to mind.

One option is to define TransferType based on an object type containing the values like Record<string, TypeFoo>, as shown here:

interface TransferRecords { 
    INTERNAL: Record<string, TypeFoo>;
    WITHDRAWAL: Record<string, TypeBar>;
    DEPOSIT: Record<string, TypeBar>;
}

type TransferType = keyof TransferRecords;

export interface EventsTooltip extends TransferRecords {
    some: string;
    extra: number;
    keys: boolean;
};

Playground Link

This way, both EventsTooltip and CardType stay in sync since they reference the same source information (TransferRecords).

If this approach doesn't fit your needs, another method entails creating a structure where the usage could fail rather than defining the type itself to fail, like so:

type TransferType = "INTERNAL" | "WITHDRAWAL" | "DEPOSIT";

type BaseEventsTooltip = {
    some: string;
    extra: number;
    keys: boolean;
};

export type EventsTooltip = BaseEventsTooltip & {
    [key in TransferType]: key extends "INTERNAL"
        ? Record<string, TypeFoo>
        : key extends "WITHDRAWAL"
            ? Record<string, TypeBar>
            : key extends "DEPOSIT"
                ? Record<string, TypeBar>
                : never;
};

Even though this solution may seem cumbersome, it ensures that if you expand TransferType, any attempt to use tooltip would result in an error indication necessitating an update to the EventsTooltip type.

Playground with tooltip functioning

Playground demonstrating how an additional element in TransferType can cause tooltip to fail

Although not the most elegant solution, it effectively serves its purpose. :-)

Answer №2

Create a generic type that performs a compile-time check on the generic input and returns it:

type TransferType = 'INTERNAL' | 'WITHDRAWAL' | 'DEPOSIT'

type TypeFoo = { __lock1: never };
type TypeBar = { __lock2: never };

type TransferEvents = Record<TransferType, Record<string, TypeFoo | TypeBar>>

type CreateEventsTooltip<
    // Compile-time check implemented here.
    T extends TransferEvents,
> = T;

export type EventsTooltip = CreateEventsTooltip<{
  some: string;
  extra: number;
  keys: boolean;
  INTERNAL: Record<string, TypeFoo>;
  WITHDRAWAL: Record<string, TypeBar>; 
  DEPOSIT: Record<string, TypeBar>;
}>;

export type EventsTooltip2 = CreateEventsTooltip<{
  some: string;
  extra: number;
  keys: boolean;
  INTERNAL: Record<string, TypeFoo>;
  WITHDRAWAL: Record<string, TypeBar>; 
  // Error occurs when "DEPOSIT" is missing
  // DEPOSIT: Record<string, TypeBar>;
}>;

TypeScript Playground Link

Edit: If you prefer index signatures with shallower typing by extending interfaces, adjust the CreateEventsTooltip type as follows:

type CreateEventsTooltip<T extends TransferEvents> = T & TransferEvents;

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

Error in Typescript: The property 'children' is not included in the type but is necessary in the 'CommonProps' type definition

Encountering this error for the first time, so please bear with me. While working on a project, I opened a file to make a change. However, instead of actually making any changes, I simply formatted the file using Prettier. Immediately after formatting, t ...

Issue with dependencies resolution in Nest framework

While delving into NestJS dependencies, I encountered an issue. As a beginner in learning Nest, I am still trying to grasp the correct way to structure everything. The problem lies in Nest's inability to resolve dependencies of the ChatGateway. It&a ...

Is it possible to indicate the base type for a generic function?

Is it possible to define the generic type T as an Object rather than a primitive type like number or string? For example, this clone function should only accept Objects as input. It will destructure the input object o, set its prototype back to that of th ...

Testing onClick using Jest when it is not a callback function in props

I have discovered various ways to utilize mock functions in jest for spying on callback functions passed down to a component, but I have not found any information on testing a simple onClick function defined within the same component. Here is an example f ...

Generate detailed documentation for the functional tests conducted by Intern 4 with automated tools

I need to automatically generate documentation for my Intern 4 functional tests. I attempted using typedoc, which worked well when parsing my object page functions. However, it failed when working with functional test suites like the one below: /** * Thi ...

Effortless code formatting with VS Code for TypeScript and JavaScript

Does anyone know of any extensions or JSON settings that can help me format my code like this: if(true) { } else { } Instead of like this: if(true){ } else { } ...

Choose the Enum in a dynamic manner

I have three enums Country_INDIA, Country_USA,Country_AUSTRALIA. During runtime, the specific country name is determined (it could be either INDIA, USA, or AUSTRALIA). Is it possible to select the correct enum based on the country name at runtime? For in ...

What could be causing the lack of updates in my SolidJS component when the data changes?

One of my components in SolidJS is an Artist page component. Here is a snippet of the code: export function Artist() { const params = useParams<{ id: string }>(); const [data, setData] = createSignal(null); createEffect(() => { fetchArti ...

Angular Unit testing error: Unable to find a matching route for URL segment 'home/advisor'

Currently, I am working on unit testing within my Angular 4.0.0 application. In one of the methods in my component, I am manually routing using the following code: method(){ .... this.navigateTo('/home/advisor'); .... } The navigateTo funct ...

Receiving an error stating "module not found" when attempting to retrieve the NextAuth session using EmailProvider in getServerSideProps

Trying to access the NextAuth session from a server-side call within getServerSideProps, using an EmailProvider with NextAuth. Referring to an example in NextAuth's documentation, I'm attempting to retrieve the session from getServerSideProps. T ...

Leveraging the Angular (2) routerLinkActive directive to handle dynamic routes

Although my current approach works, I believe there may be a more efficient way to implement this in Angular. The situation is as follows: Imagine nested, inflected paths like /logos and /logo/:id The markup below functions as intended: <li class ...

Next.js routes taking precedence over storybook routes

I recently completed a storybook project. Now, I am looking to integrate it with another project built on next.js. The issue is that Storybook and next.js each have their own set of routes. I want to streamline the routing process by utilizing next.js and ...

How to Remove onFocus Warning in React TypeScript with Clear Input Type="number" and Start without a Default Value

Is there a way to either clear an HTML input field of a previous set number when onFocus is triggered or start with an empty field? When salary: null is set in the constructor, a warning appears on page load: Warning: The value prop on input should not ...

Ensuring the correct type of keys during Object.entries iteration in TypeScript

When using Object.entries(), it returns the correct value types, but the keys are of type string[], which is incorrect. I want TypeScript to recognize my keys correctly. I attempted to use as const on the object, but it did not have any effect. Is there a ...

Utilize a variable within a regular expression

Can the variable label be used inside a regex like this? const label = 'test' If I have the regex: { name: /test/i } Is it possible to use the variable label inside the regex, in the following way? { name: `/${label}/i` } What do you think? ...

What is the method for displaying an object as JSON on the console in Angular2?

I've been utilizing a service to input my form data into an array within my angular2 application. The information is organized in the following manner: arr = [] arr.push({title:name}) After executing console.log(arr), it displays as Object. However, ...

Can you explain the distinction between any[] and [] in TypeScript?

Here is an example that successfully works: protected createGroups(sortedItems: Array<TbpeItem>): any[] { let groups: any[] = []; return groups; } However, the second example encounters a TypeScript error: type any[] not assignable to ...

Submit information by utilizing' content-type': 'application/x-www-form-urlencoded' and 'key': 'key'

Attempting to send data to the server with a content-type of 'application/xwww-form-urlencode' is resulting in a failure due to the content type being changed to application/json. var headers= { 'content-type': 'applica ...

Issues encountered while attempting to update data in angular2-datatable

Once the datatable has been rendered, I am facing an issue where I cannot update the data. I'm utilizing angular2-datatable. In my appcomponent.html file: If I try to update 'data2' in my appcomponent.ts file as shown below: this.httpserv ...

Using InjectionToken within an Angular APP_INITIALIZER function

I am currently working on implementing an APP_INITIALIZER in my Angular 10 project. The factory function I'm using has multiple dependencies, one of which is an InjectionToken<string>. However, I have encountered an issue when trying to inject i ...