Ensure the data types of two-dimensional vector elements align with the enum method arguments

In my scenario, I am dealing with two enums and a key-value store. The first level of the store is identified by Enum1, while the second level is identified by Enum2 as illustrated below.

enum X { x, y }
enum Y { z, w }

const DATA: { [key in X]?: { [key in Y]?: string } = {
  [X.x]: {
    [Y.z]: "test"
  }
}

I am aiming to define a method type signature that can verify whether a specific combination of enums is valid.

I believe it should resemble something like this:

const validateCombo = <E1 extends X, E2 extends Y>(
  arg1: E1,
  arg2: E2,
  value: (typeof DATA)[E1][E2] // This is where I run into issues
// Error message: Type 'Y' cannot be used to index type <big object type redacted>
)

The above example has been simplified for explanation purposes. Even though the provided use case may seem redundant or unrealistic, the essence lies in clarifying the core logic.

Hence, considering the given DATA variable, here are the expected outcomes:

validateCombo(X.x, Y.z, "test") // no error, since it exists in data
validateCombo(X.x, Y.w, "test") // error, combination does not exist in data
validateCombo(X.x, Y.z, "result") // error, value does not exist in data

Answer №1

The issue at hand lies in how you specified the type of MODEL to have optional properties and subproperties (indicated by the ? modifier).

Essentially, this is equivalent to:

const MODEL: Partial<Record<A, Partial<Record<B, string>>>> = ...

Hence, when you attempt deep indexing into it, the compiler will caution you about dereferencing undefined:

MODEL[A.a][B.c] // error!
//~~~~~~~~ <-- Object is possibly 'undefined'

It's important to note that since you annotated the type as potentially having missing properties, the compiler cannot ascertain if MODEL[A.a] exists or not. While you may know what you added there, the compiler doesn't. To retain this information, avoid specifying the type explicitly and let the compiler infer it.

If you want the compiler to recognize the specific types of strings (to differentiate between "test" and "result"), use a const assertion like so:

const MODEL = {
  [A.a]: {
    [B.c]: "test"
  }
} as const;

The inferred type of MODEL now becomes:

/* const MODEL: {
  readonly 0: {
      readonly 0: "test";
  };
} */

Now, we need to define a generic call signature for myMethod() based on the actual keys present at each level of the type of MODEL:

const myMethod = <
  E1 extends keyof typeof MODEL,
  E2 extends keyof typeof MODEL[E1]
>(
  arg1: E1,
  arg2: E2,
  value: (typeof MODEL)[E1][E2]
) => { }

This compiles without errors. Let's test it out:

myMethod(A.a, B.c, "test") // okay
myMethod(A.a, B.d, "test") // error
// ---------> ~~~ 
// Argument of type 'B.d' is not assignable to parameter of type 'B.c'
myMethod(A.a, B.c, "result") // error
// --------------> ~~~~~~~~
// Argument of type '"result"' is not assignable to parameter of type '"test"'

Everything seems to be working well!

Playground link to code

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

The combination of TypeScript 2.6 and material-ui 1.0.0-beta.24's withStyles with react-router withRouter is resulting in the error message: "Property 'classes' is missing in type."

Using the high order components withStyles and withRouter together has been a smooth process so far. However, after upgrading to the latest versions of these components, an error occurred. Learn more about higher-order components List of packages used: ...

Having an Empty Schema: A Guide to Configuring @ApiProperty in NestJS

When I create @ApiProperty() in my Dto, it looks like this: export class UpdateMeassageDto { @ApiProperty() message: { hash: string; created_at: Date; updated_at: Date; } } However, the result turns out to be empty. Here's what it ...

Uncovering the array within a JSON object using Angular

I am attempting to display an array in the view of my Angular project Items.json { "tipo": "texto", "texto": "A school needs to send 5 students as representatives to a forum on environmental pollution. It was decided that 2 students should be from te ...

Retrieve the value of [routerLinkActive] in the component's class

Recently, I've been working on a tab component called TabComponent and it includes the following HTML template: <a [routerLink]='link' [routerLinkActive]="[is-active]">link label</a> <button>Close tab</button> The c ...

Issue with Remix-auth verification

Recently, I added a simple login feature using remix-auth and prisma. However, in an unusual manner, within my action function, the correct credentials trigger a catch block error with a Response. Everything works fine when incorrect credentials are entere ...

Error encountered during NextJS build - file or directory not found for 500.html

I recently made the switch from Page Router to App router. Transitioning pages - 500.tsx to app - 500 - page.tsx However, I encountered an error when running yarn build/next build: > Build error occurred [Error: ENOENT: no such file or direc ...

What is the process of mapping an object from a JSON file to a different object?

Displayed below is my json file { "data": [ { "firstName": "Tom", "lastName": "Yoda", "type": "guest", "id": "0", "gender&quo ...

How to compare various values from two different Objects and then store them in an array-type variable

Below are two sets of data for objects: { "obj1": { "product": "Book", "category": "sci-fi", "title": "interstellar", }, "obj2": { & ...

Spotify Auth access token returned with error code 400

Obtaining Spotify access tokens and refresh tokens for my React Native application has been a challenging task. I followed the guide at to retrieve the required code and state using useAuthRequest. However, every attempt to exchange the code for access an ...

What is preventing the return type of document.createElement from being designated to a specific generic that is limited to HTMLElement?

Currently, I am in the process of developing a function called clone(el). This function needs to accept an HTMLElement or any subtype of it and return the same type that is provided. Can someone assist me in correctly structuring the generic signature for ...

Plugin for managing network connectivity in Ionic framework

In order to check if internet and id connection are available, I need to make a server request. I have implemented the Ionic Native Network Plugin following their official documentation. Here is my code snippet: import { Component } from '@angular/c ...

What is the purpose of utilizing import and require() in Node.js programming?

Currently, I am analyzing the source code found at "Type definitions for Express 4.16" and stumbled upon this interesting line (#18): import serveStatic = require("serve-static"); I couldn't help but wonder why the above code is necessary or being u ...

Determining the appropriate generic type in Typescript

In my code, there is a method designed to extend an existing key-value map with objects of the same type. This can be useful when working with database query results. export function extendWith< T extends { id: string | number }, O = | (T[" ...

In React + TypeScript, learn how to effectively pass down a function from a container component to a

I am currently working on developing a tabs application using React and Typescript. The main component, Tabs, manages the state and passes it down to the Content component. Additionally, I have implemented a function called 'handleName' which is ...

React Hook: Child Component's onComplete Callback Cannot Call Parent Component's Dispatch

I've attempted multiple solutions to address this issue, but none have proven successful. In my scenario, a third-party library makes an asynchronous call to load content for rendering in the DOM. To integrate this functionality, I have a component ...

What steps can be taken to avoid repetitive user creation in Firebase?

While developing an event app using the Ionic 4 framework with Firebase as the back-end, I ran into an issue with user creation on the Cloud Firestore database. Every time a user logs in using Google or Facebook, Firebase creates a new entry in the databas ...

Dealing with Undefined Issue in Angular Subscription Validation

Let me start by acknowledging that while there are numerous questions on this topic, none of them addressed the specific issue I encountered. Despite trying various approaches with maps and other methods, I am still unsure about the correct way to proceed. ...

typescript decorator implemented on a class that is nested within another class

Is it possible to decorate a nested property within a class? Let's explore with an example: function log(name: string = 'DecoratedProp') { return function logHandler(target: any, field: any) { // get the key ...

Developing custom events in an NPM package

Developing a basic npm package with signalr integration has been my recent project. Here's how it works: First, the user installs the package Then, the package establishes a connection using signalr At a certain point, the server triggers a function ...

dependency tree resolution failed - ERESOLVE

I've been attempting to run an older project on my new system, but when running npm install, I keep encountering the following issue: https://i.sstatic.net/3AgSX.png Despite trying to use the same versions of Node and NPM as my previous system, noth ...