Home > Software engineering >  How to get typeof key in array of objects?
How to get typeof key in array of objects?

Time:12-29

Given

type PageInfo = {
    title: string
    key: string
}

const PAGES: PageInfo[] = [
    {
        key: 'trip_itinerary',
        title: "Trip Itinerary",
    },
    {
        key: 'trip_details',
        title: "Trip Details",
    },
    {
        key: 'passenger_info',
        title: "Passenger Information",
    },
]

Can I pull out a type for the key key like 'trip_itinerary'|'trip_details'|'passenger_info' ? I want to use it as the key for a Record, but I'd rather not write it manually.

Something like

type PageKey = typeof PAGES[???].key

I can swap PAGES w/ satifies if it helps...

const PAGES = [
    ...
] satisfies PageInfo[]

CodePudding user response:

If you annotate PAGES as PageInfo[] then the compiler will dutifully widen PAGES to that type, allowing you to push() arbitrary PageInfo instances onto it, or rearrange its contents, or change the key and properties on any of its elements. The compiler intentionally forgets about the specific order or contents of PAGES and their particular properties, since these are subject to change. But that's not what you want.

Instead you want the compiler to infer the type of PAGES. And if you want the inferred type to reflect literal types of the key properties from the initializer, you will probably want to use a const assertion to give the compiler a hint that you care about such specifics:

const PAGES = [
    {
        key: 'trip_itinerary',
        title: "Trip Itinerary",
    },
    {
        key: 'trip_details',
        title: "Trip Details",
    },
    {
        key: 'passenger_info',
        title: "Passenger Information",
    },
] as const;

Now PAGES has the type

/* const PAGES: readonly [{
    readonly key: "trip_itinerary";
    readonly title: "Trip Itinerary";
}, {
    readonly key: "trip_details";
    readonly title: "Trip Details";
}, {
    readonly key: "passenger_info";
    readonly title: "Passenger Information";
}] */

and you can compute PageKey as follows:

type PageKey = typeof PAGES[number]["key"];
// type PageKey = "trip_itinerary" | "trip_details" | "passenger_info"

This works as desired, and you could stop there if you want.

Still, the compiler won't care whether PAGES is an array of PageInfo elements or not. If you want the compiler to verify that type this without widening to it, you can use the satisfies operator:

const PAGES = [
    {
        key: 'trip_itinerary',
        title: "Trip Itinerary",
    },
    {
        key: 'trip_details',
        title: "Trip Details",
    },
    {
        key: 'passenger_info',
        title: "Passenger Information",
    },
] as const satisfies readonly PageInfo[];

Note that we had to write satisfies readonly PageInfo[] instead of satisfies PageInfo[]. That's because the readonly X[] array type is wider than the read-write X[] array type. Since as const produces readonly arrays, then it isn't necessarily the case that PAGES satisfies PageInfo[].

Anyway now the compiler will complain if you fail to initialize PAGES with valid PageInfo elements:

const PAGES_OOPS = [
    {
        key: 'trip_itinerary',
        title: "Trip Itinerary",
    },
    {
        key: 'trip_details',
        title: 123, // oops
    },
    {
        key: 'passenger_info',
        title: "Passenger Information",
    },
] as const satisfies readonly PageInfo[] // error!
// Type '{ readonly key: "trip_details"; readonly title: 123; }' 
// is not assignable to type 'PageInfo'.

Playground link to code

  • Related