Andrew McFaul
Andrew McFaul

Reputation: 203

Typescript - create a type from certain key of array

If I have a type like

export interface MyObject {
  id: number
  title: string
}

and I create an array like this

const myArray: MyObject[] = [
  {
    id: 2358,
    title: 'Item 1'
  },
  {
    id: 85373,
    title: 'Item 2'
  }
]

Is there a way to define a type that only allows values that have appeared as an id in the array? So in this case it would look like.

type DesiredType = 2358 | 85373

Here is a link to a Codewich where I have tried a few things to no avail.

Upvotes: 0

Views: 1904

Answers (2)

fog
fog

Reputation: 3391

You can define a new type that has the type of the id property using, for example, Pick:

type MyObjectIdType = Pick<typeof myArray[number], 'id'>;

The problem here is that the array is a dynamic value and the compiler will infer the type as the most generic one, i.e., number. To have the compiler use the actual values of the id property you must tell it that the array is, in fact, a compile-time constant:

const myArray = [
    {
        id: 2358,
        title: 'Item 1'
    },
    {
        id: 85373,
        title: 'Item 2'
    }
] as const;

Note that for this to work I had to remove the type declaration MyObject[] from myArray, because a generic array of MyObject it is not compatible with the constant declaration (it may have more or less than 2 elements, with ids different from the given ones).

Obviously, this means that if you want to operate on a dynamic array, generated at runtime (and I expect you'd want), this will not work.

Upvotes: 1

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249536

To get the type of id in myArray you can use a type query: typeof myArray[number]['id']. The problem is that the type of id is not preserverd in the type of myArray.

To preserve the type you can use as const (but you loose the MyObject constraint)

const myArray = [
    {
        id: 2358,
        title: 'Item 1'
    },
    {
        id: 85373,
        title: 'Item 2'
    }
] as const;

type Id = typeof myArray[number]['id']

Playground Link

Or you can use a helper function:

function makeMyObjectArray<V extends number>(...a: Array<MyObject & { id: V}>) {
    return a;
}
const myArray = makeMyObjectArray(
    {
        id: 2358,
        title: 'Item 1'
    },
    {
        id: 85373,
        title: 'Item 2'
    }
)

type Id = typeof myArray[number]['id']

Playground Link

Upvotes: 3

Related Questions