Search code examples
javascriptarraystypescriptobjectpartials

Create a Record from an object of arrays variable in typescript


I have a variable that is an object of arrays:

cssColorsObject = {
    red: ["lightsalmon", "salmon", "darksalmon", "lightcoral", "indianred", "crimson", "firebrick", "red", "darkred"],
    orange: ["coral", "tomato", "orangered", "gold", "orange", "darkorange"],
    yellow: [...
}

I would like to create a type/interface for future object variables that contain some of the keys (eg "red", "orange", "yellow"), and some or all of that key color's properties, eg:

{
    red: ["lightsalmon", "salmon"], // OK
    orange: [], //OK
    yellow: ["lightsalmon"], // NOT OK - because light salmon is a color associated with the "red" key in the original object
}

I have worked out the following:

type CssColorKeys = keyof typeof cssColorsObject; // = "red" | "orange" | "yellow" | "green" , etc
type CssColorValues = typeof cssColorsObject[CssColorKeys][number]; // = "lightsalmon" | "salmon"... | "maroon"
type NewRecord = Record<CssColorKeys, CssColorValues> // Not what I want as this requires all properties and the exact same array.

I imagine I need to use partials, but I'm struggling to implement them here.


Full credit to @captain-yossarian for answer below. I just wanted to add a simplified solution, based on his answer:

type CssColorsPartial = Partial<{
    [Prop in keyof typeof cssColorsObject]: Array<typeof cssColorsObject[Prop][number]>;
}>;

I've created another type for a mapped type with mandatory keys and optional array values here


Solution

  • First of all, you need to make cssColorsObject immutable.

    const cssColorsObject = {
        red: ["lightsalmon", "salmon", "darksalmon", "lightcoral", "indianred", "crimson", "firebrick", "red", "darkred"],
        orange: ["coral", "tomato", "orangered", "gold", "orange", "darkorange"],
        yellow: ['lemon']
    } as const
    

    Then iterate through cssColorsObject keys and create the desired type.

    type PartialCSS<T extends Record<string, ReadonlyArray<string>>> =
        Partial<{
            [Prop in keyof T]: Array<T[Prop][number]>
        }>
    

    I have used have unpacked T[Prop] array into new similar Array<T[Prop][number]> because T[Prop] is immutable tuple where order of elements is preserved whereas new type does not require exact element order. Drawback, it allows you to use two similar elements.

    Whole code:

    const cssColorsObject = {
        red: ["lightsalmon", "salmon", "darksalmon", "lightcoral", "indianred", "crimson", "firebrick", "red", "darkred"],
        orange: ["coral", "tomato", "orangered", "gold", "orange", "darkorange"],
        yellow: ['lemon']
    } as const
    
    
    type PartialCSS<T extends Record<string, ReadonlyArray<string>>> =
        Partial<{
            [Prop in keyof T]: Array<T[Prop][number]>
        }>
    
    const result: PartialCSS<typeof cssColorsObject> = {
        red: ['salmon'],
        yellow:['salmon'] // expected error
    }
    

    Playground