Infer type from array value

356 views Asked by At

Using Typescript 3.8.3.

I'm trying to set up types for the following data, but cannot for the life of me figure it out.

type Info = {
  title: string;
  description: string;
    items: Array<Item<object>>;
}

type Item<T> = {
  title: string;
  data: T;
}

const info: Info = {
  title: 'some title',
  description: 'some description',
  items: [
    { title: 'title1', data: { param1: 'something', param2: 'something else' } },
    { title: 'title2', data: { param3: 'abc' } },
    { title: 'title3', data: { param1: 'not the same as above, just the key name', param4: 123 } }
  ]
};

info.items[0].data.param1 // Property 'param1' does not exist on type 'object'

I know Array<Item<object>> is wrong, but I can't figure out how I can infer type of the union of an array of elements.

Is what I want to achieve even possible?

2

There are 2 answers

1
HTN On

The only thing you can do is:

type Info = {
  title: string;
  description: string;
    items: ReadonlyArray<Item<object>>; // Only work with ReadonlyArray
}

type Item<T> = {
  title: string;
  data: T;
}

const info = {
  title: 'some title',
  description: 'some description',
  items: [
    { title: 'title1', data: { param1: 'something', param2: 'something else' } },
    { title: 'title2', data: { param3: 'abc' } },
    { title: 'title3', data: { param1: 'not the same as above, just the key name', param4: 123 } }
  ]
} as const;

info.items[0].data.param1 // 'something' as info type is known
const anotherInfo: Info = info // info const be assigned to Info

If info is not 'const' type, you can modify it like: info.items = [], so typescript cannot guarantee the value / type of info.items[0].data.param1

1
Pat On

Would this work?

type Info<T> = {
  title: string;
  description: string;
  items: Item<T>[];
}

type Item<T> = {
  title: string;
  data: T;
}

const info = {
  title: 'some title',
  description: 'some description',
  items: [
    { title: 'title1', data: { param1: 'something', param2: 'something else' } },
    { title: 'title2', data: { param3: 'abc' } },
    { title: 'title3', data: { param1: 'not the same as above, just the key name', param4: 123 } }
  ]
};

info.items[0].data.param1 // string | undefined