Reputation: 3947
Is it possible in TypeScript, from this data:
const data = {
field1: {values: ['a', 'b', 'c']},
field2: {values: ['c', 'd', 'e'], multiple: true}
}
const fields = someFunction(data)
To infer/derive the following return value of someFunction
(and hence type of fields
), based on whether multiple
exists or not (or if it's true or not, if that makes things easier)?
type FieldsType = {
field1: 'a' | 'b' | 'c',
field2: ('c' | 'd' | 'e')[]
}
data
can be as const
if that's what's required.
Upvotes: 0
Views: 1108
Reputation: 187014
First you want a type for your source data:
type Data = Record<string, {
values: readonly string[],
multiple?: boolean
}>
Then you can make a mapped conditional type that accepts that:
type Fields<T extends Data> = {
[K in keyof T]:
T[K]['multiple'] extends true
? T[K]['values'][number][]
: T[K]['values'][number]
}
This type maps over all keys in T
(field1
and field2
), and resolve the value type of that property as a conditional type.
That conditional type says that if T[K]['multiple'] extends true
then returns an array of the member type of the values
of that property, else return a non-array of that type.
You can now use that type like:
function someFunction<T extends Data>(data: T): Fields<T> {
// TODO: implement this
}
Which does what you expect:
const fields = someFunction({
field1: { values: ['a', 'b', 'c'] },
field2: { values: ['c', 'd', 'e'], multiple: true }
} as const)
fields.field1 // ('a' | 'b' | 'c')[]
fields.field2 // 'a' | 'b' | 'c'
Upvotes: 3