Home > other >  Allow only certain type for specific object key in typescript
Allow only certain type for specific object key in typescript

Time:10-15

I have the following categories of Pet:

type Pet = 'dog' | 'cat'

Then I have types for allowed DogNames and CatNames respectively:

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

I want to type the following object so that only cat names are allowed under cat and dog names are only allowed under dog, but I also don't want new pet categories to be invented unless they are in the type:


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
   }
}

The above does not work, specifically the part of "dog" in Pet

CodePudding user response:

Use, for the pet type, the same mapped structure you are using for the names, and get the associated names with a conditional type.

As suggested by caTS, consider changing the conditional to a lookup table for performance and maintainability in the future when there are more members of the union.

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

CodePudding user response:

The first thing you want to do is tell TypeScript that pets should be of type Pets. :-)

Then the pedestrian way to do this is to use Partial<Record<___, boolean>>:

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

Playground link (I made the capitalization of KittyKat consistent)

Obviously, you'll need to add to Pets if you add to Pet, but that'll be the case in any solution.

  • Related