Limit the type to be used for a particular object key in TypeScript

My pet categories consist of 'dog' and 'cat' as defined in the Pet type:

type Pet = 'dog' | 'cat'

I also have distinct types for allowed names for dogs and cats:

type DogName = 'Jack' | 'Fenton' type CatName = 'Priscilla' | 'Kittykat'

To ensure that only valid names are assigned to the respective pets, I am trying to create a type for an object where cat names are only allowed under 'cat' and dog names under 'dog'. Additionally, I want to restrict the creation of new pet types that do not match the existing ones:


type Pet = 'dog' | 'cat'
type DogName = 'Jack' | 'Fenton'
type CatName = 'Priscilla' | 'Kittykat'

type Pets = {
   ["dog" in Pet]: {[key in DogName]: boolean}
   ["cat" in Pet]: {[key in CatName]: boolean}
}

const pets = {
   dog: {
      Jack: true
   },
   cat: {
      KittyKat: true
   }
}

However, there seems to be an issue with the code snippet specifically at "dog" in Pet.

Answer №1

Utilize the same structured mapping approach for pet types as you do for names, and retrieve the corresponding names using a conditional type.

Following advice from caTS, consider switching the conditional to a lookup table to enhance performance and manageability in the future as more union members are added.

type Pet = 'dog' | 'cat'
type DogName = 'Jack' | 'Fenton'
type CatName = 'Priscilla' | 'Kittykat'

type NameFromPetType<PetType extends Pet> = PetType extends 'dog' ? DogName : CatName;
type Pets = {
   [PetType in Pet]: {[key in NameFromPetType<PetType>]?: boolean}
}

TS Playground

Answer №2

To start off, the first step is informing TypeScript that pets must adhere to the type Pets. :-)

The conventional approach to accomplish this is by utilizing

Partial<Record<___, boolean>>
:

type Pets = {
    dog: Partial<Record<DogName, boolean>>;
    cat: Partial<Record<CatName, boolean>>;
};

Playground link (I ensured consistency in the capitalization of KittyKat)

It goes without saying that when you expand on Pet, you will also need to update Pets, but this remains true for any solution.

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

React Hot Toast useState is unfortunately not exported from the React library

While working on a Next.js project, I encountered an issue when trying to use react-hot-toast. When I attempted to import it into any file, I received the following error message: Error - ./node_modules/react-hot-toast/dist/index.mjs Attempted import erro ...

The 'checked' property cannot be bound to 'mat-button-toggle' as it is not recognized as a valid property in Angular 9

I am encountering an issue with my Angular 9 application. I have integrated angular-material and imported the MatCheckboxModule correctly in the module. Here is the version of the material package I am using: "@angular/material": "^10.2.0&q ...

Since transitioning my project from Angular 7.2 to Angular 8, I noticed a significant threefold increase in compile time. How can I go about resolving this issue

After upgrading my project to Angular 8, I noticed a significant increase in compile time without encountering any errors during the upgrade process. Is there a way to restore the previous compile time efficiency? **Update: A bug has been identified as th ...

The deployment on Heroku is encountering issues due to TypeScript errors related to the MUI package

As someone relatively new to TypeScript and inexperienced in managing deployments in a production setting, I've been working on a project based on this repository: https://github.com/suren-atoyan/react-pwa?ref=reactjsexample.com. Using this repo has a ...

Compelling users to provide feedback on an App with the Ionic Framework

As a novice developer, I could use some assistance with implementing ratings in my app. My goal is to show menu items based on whether a user has given my app a 5-star rating. For instance, if a user gives a 5-star rating, I would assign the class "review ...

Expanding User Type to Include Extra User Data Using NextAuth.js

I encountered a dilemma where I needed to include the "profile" property in my section object to cater to different user personas in my application. However, despite retrieving the logged-in user's data from the backend and storing it in the NextAuth. ...

Converting an HTML page to PDF with Angular using jsPdf and Html2Canvas

[1st img of pdf generated with position -182, 0, image 208,298 ][1]Converting an HTML page to PDF in Angular 8+ using jspdf and Html2canvas has been a challenge. Only half of the page is successfully converted into PDF due to a scaling issue. Printing th ...

Ensure that a variable adheres to the standards of a proper HTML template

I'm struggling with a problem in my Angular application. I need to ensure that a TypeScript variable is a valid HTML template to compile successfully, like this: let v = '<div>bla…</div>' However, if the variable contains inco ...

Customized interfaces utilizing generic components

Here is my simplified question. interface Identity{ name: string; } Next, we have a generic interface. interface State<T extends Identity>{ [T.name] : StateContainer<T> } Unfortunately, this approach fails and the following error is ...

The routing navigate method is failing to direct to the desired component

For the past day, I have been struggling to find a solution to my issue but without success. The routing seems to be malfunctioning as it keeps showing the HTML content from app.component.html even when I try changing the path in the URL. Here is a snippe ...

The type '{}' cannot be assigned to type 'IntrinsicAttributes & FieldsProp'. This error message is unclear and difficult to understand

"The error message "Type '{}' is not assignable to type 'IntrinsicAttributes & FieldsProp'.ts(2322)" is difficult to understand. When I encountered this typeerror" import { useState } from "react"; import { Card } fr ...

AsExpression Removes Undefined from Type More Swiftly Than I Prefer

Utilizing an API that returns a base type, I employ the as keyword to convert the type into a union consisting of two sub-types of the original base type. interface base { a: number; } interface sub1 extends base { s1: number; } interface sub2 extends bas ...

The Vue Router hooks are not being activated within the component when utilizing Typescript

I've been pondering this issue for quite some time. Despite my extensive search efforts, I am still unable to figure out why the route-hooks are not working in my component: 1. The component is being loaded from RouterView: <router-view class="z1 ...

What is the best way to insert CSS code into a custom Vue directive file?

I need a solution that applies a gradient background to the parent div in case an image fails to load. I've attempted to create a directive for this purpose: export default { bind(el: any, binding: any) { try { ..... ...

Include a log out option in the side menu of the ionic2 application

I am working with the sidemenu template to kick off my application. I aim to incorporate a button within the sidemenu that enables users to trigger a modal alert for confirming logout. Below is the code snippet: app.component.ts: import { Component, View ...

Guide on streamlining interface initialization within a React project using Typescript

Working on my Typescript project, I consistently utilize an interface within the State of various components: interface Item { selectedValue: string originalSelectedValue: string loading: boolean disabled: boolean isValid?: boolean } ...

What is the proper way to define the scope for invoking the Google People API using JavaScript?

I am attempting to display a list of directory people from my Google account. export class People { private auth: Auth.OAuth2Client; private initialized: boolean = false; private accessToken: string; constructor(private readonly clientEmail: strin ...

The instanceof operator does not recognize the value as an instance and is returning false, even though it

Is there a method to verify the current instance being used? This is what I am logging to the console: import { OrthographicCamera } from 'three'; // Later in the file: console.log(camera instanceof OrthographicCamera, camera); and the result ...

What is the process for modifying object information using string input in typescript?

How can I update an object's value in TypeScript based on a string input related to the object key? const objData = { // random value A: 11, B: 13, C: 53, innerObj: { AStatus: true, BStatus: false, CStatus: true, }, }; type Item ...

Is there a way to retrieve the Incoming Message object in Angular using HttpClient?

From my Angular file, I am sending this request to the server. Is there a way to access it from my typescript file using a HttpClient object? IncomingMessage { ... // Headers details omitted for brevity url: '/teradata/databases/view/djfg', ...