I'm trying to create an object interface (transforms), that has keys which are properties of a generic object (T), so that the keys can map to a function that passes the property value of T specific to the key it was passed.
interface Options<T> {
headers: (keyof T)[]
filename: string
items: T[]
transforms?: Partial<{
[field in T as keyof T]: (item: T[field extends keyof T ? field : never]) => string
}>
}
As of now, item always resolves to never. I want it to be able to resolve to T[field] or rather T[keyof T] specific to the key it's attached to.
EDIT
Here is a better example of what I was trying to accomplish. I'm trying to make sure that the arguments passed to the functions in the transforms object, which have keys that are properties of T, have the correct type (T[keyof T]). In the corrected example below, color should be a string, and isTasty should be a boolean when passed as arguments in "transforms".
interface Food {
isTasty: boolean
color: string
}
interface Options<T> {
headers: (keyof T)[]
filename: string
items: T[]
transforms?: Partial<{
?
}>
}
function Foo<T>({headers, filename, items, transforms}: Options<T>){
return 'bar'
}
Foo({
headers: ['isTasty', 'color'],
filename: 'test',
items: [{color: 'red', isTasty: true}] as Food[],
transforms: {
color: (color) => '#' color,
isTasty: (isTasty) => isTasty ? 'Yes' : 'No'
}
})
CodePudding user response:
You just want the transforms
property to be a mapped type over the properties of T
. Writing {[K in keyof T]: F<K>}
will produce a new type with the same keys as T
but whose properties are F<K>
for each key type K
. In your case, you want each property to be a function that accepts a value of the type corresponding to the property value of T
at the key K
. That is, a function that accepts a value of the indexed access type T[K]
. Like so:
{ [K in keyof T]: (property: T[K]) => string }
Note that if you want to apply the Partial<T>
utility type to it, you can write this more simply with the optional mapping modifier (?
):
{ [K in keyof T]?: (property: T[K]) => string }
That gives you the following definition for Options<T>
:
interface Options<T> {
headers: (keyof T)[]
filename: string
items: T[]
transforms?: {
[K in keyof T]?: (property: T[K]) => string
}
}
And then everything works as you want:
function foo<T>(o: Options<T>) { }
foo({
headers: ['isTasty', 'color'],
filename: 'test',
items: [{ color: 'red', isTasty: true }],
transforms: {
color: (color) => '#' color,
isTasty: (isTasty) => isTasty ? 'Yes' : 'No'
}
})