Ensure that the types of one field are restricted to only keys that exist in an array of another field

Is there a way to restrict the keys of an object so that they must match those declared in an array of a union type?

type Category = 'software' | 'hardware' | 'books'

type Item = {
    categories: Category[];
    registry: Partial<{[key in Category]: string}>
}


//This code should result in an error because 'books' is not included in the categories array:
const myInValidItem: Item = {
    categories: ['software', 'hardware'],
    registry: { books: 'blabla' }
}

Access Playground Link

Answer №1

The simplest solution may be to create a helper function that verifies the types of related registry and categories:

function createParts<T extends Category[]>(
  categories: T, registry: { [K in T[number]]?: string }
) {
  return { categories, registry };
}

const myInValidItem: Item = createParts(
  ["software", "hardware"],
  { books: "blabla" }
) // error, books not included

You can also define a more specific type for Item and use it as the parameter for the function:

const myInValidItem = {
    categories: ['software', 'hardware'],
    registry: { books: 'blabla' }
} as const // using const assertion to maintain tuple type

type ConstrainedItem<T extends readonly Category[]> = {
    categories: T;
    registry: Partial<{ [key in T[number]]: string }>
}

declare function doSomething<T extends readonly Category[]>(item: ConstrainedItem<T>): void

doSomething(myInValidItem) // error
doSomething(myValidItem)

Playground 1, Playground 2

Answer №2

Similar to the suggestion made by ford04, here is a slightly different way of interpreting the solution:

type Category = 'software' | 'hardware' | 'books';

const createItem = <T extends Category>(item: {
  categories: T[];
  registry: { [key in T]?: string }
}) => item;

//This should be considered invalid because 'books' is not included in the categories array:
const myInvalidItem = createItem({
  categories: ['software', 'hardware'],
  registry: { 'books': 'blabla' }
});

Link to Playground

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

Ensuring consistency between TypeScript .d.ts and .js files

When working with these definitions: https://github.com/borisyankov/DefinitelyTyped If I am using angularJS 1.3.14, how can I be certain that there is a correct definition for that specific version of Angular? How can I ensure that the DefinitelyTyped *. ...

Angular2 - Utilizing the "withCredentials" Request Setting

I encountered a CORS problem while attempting to connect to Neo4j in an Angular2 component: Response to preflight request doesn't pass access control check. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header ...

Is there a way in TypeScript to automatically assign object properties to a class in an efficient manner?

Imagine a scenario where I have a unique class structured like this: class Organization { title: string; website: string; location: string constructor() { } } Now, picture retrieving an object from a database that is known to conta ...

Tips for patiently waiting for a method to be executed

I have encountered a situation where I need to ensure that the result of two methods is awaited before proceeding with the rest of the code execution. I attempted to use the async keyword before the function name and await before the GetNavigationData() me ...

Are you ready to put Jest to the test by checking the completion event of

The RxJS library's Observer triggers three main events: complete error next If we want to verify the occurrence of the complete event using Jest, how can this be achieved? For instance, we are able to test the next and error events by checking for ...

It is true that variable is of type "function", however, it does not have a call signature as expected because of the unexpected type merging

Similar to React, I am interested in working with states in a custom library. The current class I have is as follows: export abstract class Room<State> { protected state: State; protected setState<Key extends keyof State>( sta ...

ReactJS: What happens when props aren't in array form?

Newcomer question: Just starting to dive into ReactJS+Typescript. I'm working with a simple interface that describes an array of data: interface IProfile { name: string; avatar_url: string; company: string; } const testData: IProfile[] = [ { ...

Place the Div in a consistent position on images of varying widths

I have a dilemma with my class that is supposed to display images. The issue arises when I try to place a div within an image inside this class. Everything works smoothly when the image takes up the entire width of the class, but as soon as the image widt ...

TypeScript's TypeGuard wandering aimlessly within the enumerator

I'm puzzled by the fact that filter.formatter (in the penultimate line) is showing as undefined even though I have already confirmed its existence: type Filter = { formatter?: { index: number, func: (value: string) => void ...

Retrieving data from array services in Angular using Typescript

I need help retrieving data from an array in my services using a get function. I've tried using the .filter and .find functions, but I'm struggling with the execution and haven't been able to retrieve the data successfully. I know this may b ...

What is the process for defining global type aliases in TypeScript?

One of the aliases I use is type ReactMouseEvent = React.MouseEvent<HTMLDivElement, MouseEvent> To implement this alias, I created a globals.d.ts file within the types folder of my project: // in globals.d.ts import React = require('react' ...

Parallel Execution Issue with RxJS Observable forkJoin

Struggling to understand why my requests aren't executing concurrently with the following code. As a newcomer to RxJS and observables, I would greatly appreciate any guidance on improving this snippet below. Essentially, I am fetching data from a REST ...

Creating templates for both classes and individual objects is an essential part of object-oriented programming

I am working on a simple game project using TypeScript. My goal is to utilize interfaces to implement them in classes and pass them as arguments for creating new instances of a class. interface ObjectConstructor { element: HTMLElement; x_pos: numbe ...

How can I eliminate specific text from a string following a designated character in Angular 8 with the help of *ngFor?

Hello everyone, I am new to working with Angular and could use some assistance. I have retrieved data in my application through an API response, which is in the format of an array of objects. I have successfully used *ngFor to iterate over this data. Wit ...

What could be causing the Google OAuth callback to fail upon the initial login attempt on my site?

I have developed a website that allows users to log in with Google. The following code is used to verify if the user has logged in before. If they have, they are simply logged into the site. However, if it's their first time logging in, they are added ...

Converting data types of a destructured property

In my Next.js application, I'm using a router hook and destructuring like this: const { query: { run_id }, } = useRouter(); The type of `run_id` is as follows: run_id: string | string[] | undefined I tried to typecast it as shown below, but it doe ...

Ensuring accurate validation of function arguments in Typescript

Today is my second day diving into the world of typescript and I find myself puzzled by a particular situation: Let's imagine we have a file named .env which contains a variable called ACCESS_TOKEN_SECRET ACCESS_TOKEN_SECRET = 9i0d98f7a0sd87fy03eihdq ...

What is the best way for me to create a test for GTM tracking?

Currently, I am in the process of developing an application using NextJS/React and I have integrated GTM for tracking customer events. One specific event I am tracking is when a customer clicks on the 'Add to Cart' button on the product page. Wh ...

Implementing Dynamic Route Based Modals in Next.js

Is it possible to create a dynamic route-based modal in Next.js 13 (using the app directory)? My goal is to achieve a similar functionality to websites like nomadlist and Instagram, where clicking on a card opens a modal with an updated route, such as htt ...

Can we limit a specific key to only accept a specific value?

Is it possible to restrict the type of a certain key to a certain value in TypeScript for an object? For instance, consider the following example: interface ValueType1 { key1: string, key2: string, } interface ValueType2 { key3: number, ...