Patrick Hund
Patrick Hund

Reputation: 20276

How to define type for an object with arbitrary keys out of a set of valid keys?

According to this example from the TypeScript documentation, if I want to define a type for objects with multiple properties that all have the same type, I can use the Record utility type, like this:

interface CatInfo {
  age: number;
  breed: string;
}

type CatName = 'miffy' | 'boris' | 'mordred';

type Cats = Record<CatName, CatInfo>;

function logCat(cats: Cats, catName: CatName) {
  console.log(cats[catName]);
}

const myCats = {
  miffy: { age: 10, breed: 'Persian' },
  boris: { age: 5, breed: 'Maine Coon' },
  mordred: { age: 16, breed: 'British Shorthair' }
};

logCat(myCats, 'boris');

This works well, as long as my object myCats has properties for each of the cat names defined in the type CatName.

However, when I send an object to logCat that does not contain cat info for all of the cat names, like this:

logCat(
  {
    boris: { age: 5, breed: 'Maine Coon' },
    mordred: { age: 16, breed: 'British Shorthair' }
  },
  'boris'
);

…I get an error from the TypeScript compiler, complaining that my cats object does not contain any info for cat “miffy”:

error message from TS compiler

I've tried to do something clever like this to fix it, but that didn't work:

type Cats = Record<Partial<CatName>, CatInfo>;

How can I define a type that allows me to control which property names for an object are valid, whilst not all property names need to be used in the object?

Upvotes: 2

Views: 144

Answers (1)

Pete TNT
Pete TNT

Reputation: 8403

You can set the type of the parameter cats to Partial<Cats>.

function logCat(cats: Partial<Cats>, catName: CatName) {
  console.log(cats[catName]);
}

Works:

logCat(myCats, 'boris');

logCat(
  {
    boris: { age: 5, breed: 'Maine Coon' },
    mordred: { age: 16, breed: 'British Shorthair' }
  },
  'boris'
);

Fails:

logCat(
  {
    boris: { age: 5, breed: 'Maine Coon' },
    mordred: { age: 16, breed: 'British Shorthair' },
    dog: { foo: 16, breed: 'Not cat' }
  },
  'boris'
);

logCat(
  {
    boris: { age: 5, breed: 'Maine Coon' },
    mordred: { age: 16, breed: 'British Shorthair' },
    dog: { age: 16, breed: 'Not cat' }
  },
  'boris'
);


logCat(
  {
    boris: { age: 5, breed: 'Maine Coon' },
    mordred: { age: 16, breed: 'British Shorthair' },
    'dog'
  },
  'boris'
);

Upvotes: 2

Related Questions