3

How can I define a type for my typescript object where some of the keys are known, others are either unknown or defined as a separate type.

Take the following object:

{
  foo: 1,
  bar: "abc",
  baz: {
    foobar: 2
  },
  foobaz: {
    foobar: 1
  }
}

I know the object will always have the properties foo and bar as defined in this type:

type MyObject = {
  foo: number;
  bar: string;
}

But I know that the object may have other properties and if they do, they will always have the same structure of an object with a property foobar and a value of the type number.

How do I defined this type?

I tried:

type MyObject = {
  foo: number;
  bar: string;
  [key: string]?: {
    foobar: number;
  }
}

But it appears you can't declare a generic key like that when you have explicit keys in the same type.

I actually know the possible values of the "unknown" properties. So I tried this:

type MyList = 'baz'|'foobaz';

type MyObject = {
  foo: number;
  bar: string;
  [key in MyList]?: {
    foobar: number;
  }
}

The [key in MyList] part works alone, but as soon as I combine it with the explicit properties I get an error: "'MyList' only refers to a type, but is being used as a value here". I tried the same with an Enum and got the same result.

zkwsk
  • 1,960
  • 4
  • 26
  • 34
  • So, do you know the keys or not? – Tobias S. Aug 23 '22 at 12:09
  • Unfortunately there is no specific type in TypeScript that works this way. You'd want an index signature instead of a mapped type, but you can't declare a "rest index signature". See the linked question and its answer for the situation and various workarounds and approaches. – jcalz Aug 23 '22 at 14:35

1 Answers1

1

In this case, since you know the value of the properties, they should just be optional and hardcoded to the same type.

A mapped type may not declare properties or methods.

type Inner = {
    foobar: number;
};

type MyObject = {
  foo: number;
  bar: string;
  baz?: Inner;
  foobaz?: Inner;
}

See an example of an alternative that still uses mapped types is to AND (&)two types:

type MyList = 'baz'|'foobaz';

type MyObject = {
  [key in MyList]?: {
    foobar: number;
  }
}

type MyObject2 = {
  foo: number;
  bar: string;
}

type MyObject3 = MyObject2 & MyObject;

// Alternatively

type MyObject =  {
  [key in MyList]?: {
    foobar: number;
  }
} & {
  foo: number;
  bar: string;
};

Screenshot of Typescript

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217