2

Let's see we have an union of strings:

type PossibleKeys = 'foo' | 'bar'

And we want a type which checks if an object contains no other keys than the above:

type ValidObject = { [key in PossibleKeys]: any }

This works, but it accepts other keys as well, and it requires all the keys, which can be solved with marking it as optional ?, but still accepts other keys.

What I want is to accept:

{foo: true, bar: true}
{foo: true}
{bar: true}
{}

And do not accept:

{foo: true, bar: true, baz: true}

Basically anything else which is not in the accept example.

Is that possible?

Gergő Horváth
  • 3,195
  • 4
  • 28
  • 64

1 Answers1

3

This is essentially a duplicate of another question. Translating the answer there to this question gives the following solution:

type PossibleKeys = 'foo' | 'bar'
type Exactly<T, U> = T & Record<Exclude<keyof U, keyof T>, never>;
const asValidObject =
    <T extends Exactly<Partial<Record<PossibleKeys, any>>, T>>(t: T) => t;

const a = asValidObject({ foo: true, bar: true }); // okay
const b = asValidObject({ foo: true }); // okay
const c = asValidObject({ bar: true }); // okay
const d = asValidObject({}); // okay
const e = asValidObject({ foo: true, bar: true, baz: true }); // error!
// -------------------------------------------> ~~~
// true is not assignable to never

Link to code

Hope that helps; good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360