I want to be able to construct a type that accepts the type of another object and returns a different type based on certain properties in that object. This will be easier to explain in code:
// these describe the type of inputs a UI would display
// and their returned values
// e.g. a colorHex UI control should return a string
type Inputs = {
colorHex: string
yearPicker: number
}
// colorHex | yearPicker
type InputTypes = keyof Inputs
// Describes an input. It has a type to define the UI and a label
type Input = {
type: InputTypes
label: string
}
// Describes a composition of inputs. Has an ID and an object with Inputs
type Composition = {
id: string
inputs: Record<string, Input>
}
// Describes a 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',
},
},
},
}
// I want to recieve a composition and return a map of input key and the resulting input value type
type InputProps<T extends Composition> = {
[P in keyof T['inputs']]: Inputs[T['inputs'][P]['type']]
}
// The input prop types for comps.short
type ShortProps = InputProps<typeof comps.short>
// What I want ShortProps to equal
type Expected = {
bgColor: string
year: number
}
// ultimately what I want to do with this
const someFn = (props: ShortProps) => {
// props === { bgColor: string; year: number }
}
// this is correct
someFn({ bgColor: '#000', year: 2020 })
// this is incorrect and should result in a type error
someFn({ bgColor: 0, year: '2020' })
Here is a playground link. Notice that the last line does not give a type error when it should.
CodePudding user response:
You only need to make sure that comps
satisfies (or "matches") the type Compositions
. Before this, you were overwriting the type information of the object by explicitly annotating it with Compositions
. Currently in TS 4.8, we have to use a helper function for this:
function comp<C extends Compositions>(c: C) { return c }
const comps = comp({
short: {
id: 'short',
inputs: {
bgColor: {
type: 'colorHex',
label: 'BG Color',
},
year: {
type: 'yearPicker',
label: 'Count',
},
},
},
});
However, in 4.9 , we can use satisfies
keyword:
const comps = {
short: {
id: 'short',
inputs: {
bgColor: {
type: 'colorHex',
label: 'BG Color',
},
year: {
type: 'yearPicker',
label: 'Count',
},
},
},
} satisfies Compositions;
Now you'll get an error on the last line, as desired.