2

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'
  }
})

fixed and working example

6
  • 1
    My guess is that you want this, but without a minimal reproducible example that includes at least one use case showing an input and expected output, I'm not sure. Can you edit such an example into your code? Commented May 17, 2022 at 21:02
  • Actually that's perfect. I'm not sure how I ended up making this overcomplicated. 😅 Commented May 17, 2022 at 21:06
  • Note that there's quite a bit wrong about [field in T as keyof T]: (item: T[field extends keyof T ? field : never]) => string, to the extent where I doubt it's worth explaining. Suffice it to say that it doesn't do what you think it does. Commented May 17, 2022 at 21:06
  • It's great that my suggestion works for you. For the sake of making this a good Stack Overflow question for future readers, could you still edit the question to make it clear what you're looking for? Like, have an example interface Foo {a: string, b: number, c: boolean} and then you want Options<Foo> to have a transforms property of type {a: (item: string) => string; b: (item: number) => string; c: (item: boolean) => string;}. Or any concrete use case that helps to express your intent Commented May 17, 2022 at 21:08
  • Edited the question! Hopefully thats more clear. Commented May 17, 2022 at 21:38

1 Answer 1

2

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'
  }
})

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.