In building a type, I aim to create one that can accept the type of another object and generate a different type based on specific properties in that object. Illustrating this concept through code:
// Definition representing the types of inputs shown on UI and their corresponding return values
// e.g. colorHex UI control should return a string
type Inputs = {
colorHex: string
yearPicker: number
}
// Union of possible input types - colorHex or yearPicker
type InputTypes = keyof Inputs
// Defines an input with a type for UI representation and a label
type Input = {
type: InputTypes
label: string
}
// Represents a collection of inputs containing an ID and an object of Inputs
type Composition = {
id: string
inputs: Record<string, Input>
}
// Map of compositions
type Compositions = Record<string, Composition>
const comps: Compositions = {
short: {
id: 'short',
inputs: {
bgColor: {
type: 'colorHex',
label: 'BG Color',
},
year: {
type: 'yearPicker',
label: 'Count',
},
},
},
}
// Desired output is a map of input key and the resulting
// input value type received from a composition
type InputProps<T extends Composition> = {
[P in keyof T['inputs']]: Inputs[T['inputs'][P]['type']]
}
// Expected input prop types for comps.short
type ShortProps = InputProps<typeof comps.short>;
// Expected result for ShortProps
type Expected = {
bgColor: string
year: number
};
// Function utilizing the input props as parameter
const someFn = (props: ShortProps) => {
// props === { bgColor: string; year: number }
}
// The following is correct
someFn({ bgColor: '#000', year: 2020 })
// This is incorrect and should produce a type error
someFn({ bgColor: 0, year: '2020' })
Explore the playground link. Note that the final line may not give a type error even though it should.