0

I'd like to add typings for my prop function. It's curried (key first, object last).

I tried with a following approach: after partially applying it to some key, I try to restrict it to an object containing such a property.

I tried to implement it in a following way:

const prop = (key: string) =>
  <K, T extends { [key: string]: K }>(obj: T): K =>
  obj[key]

Unfortunately, this approach deasn't seem to work. Here's a code I'd expect to fail (I also included all this code in a Typescript Playground):

const getFoo = prop('foo')
getFoo({ fooo: 42 })

with an error message like

Error: type '{ fooo: number }' is not assignable to '{ foo: number }'

However it's not the case (it fails silently). I would also expect to infer the type of property in an object, so this code fails:

const someNumber: number = prop('foo')({ 'foo': 'abc' })

with error like:

Error: type 'string' is not assignable to 'number'.

and this is working as expected. However, if I omit the type annotation on someNumber, it doesn't get infered, so for:

const someNumber = prop('foo')({ 'foo': 'abc' })

type for someNumber is unknown. Is there a way to satisfy all these constraints?

I've been also thinking to do so "the other way around", to give a type of the object we will be working on first, and then to do something with <T, K extends keyof T>, but this would force the user to provide type parameter each time...

2 Answers2

1

Try this:

const prop = <Key extends string>(key: Key) =>
  <T extends Record<Key, any>>(obj: T): T[Key] =>
  obj[key];

const getFoo = prop('foo')
const result: string = getFoo({ foo: 42 })
Colliot
  • 1,522
  • 3
  • 16
  • 29
  • Thanks. This solution works,but unfortunately it gives obscure error messages mentioning `Record`type (which may be very confusing for people non-familiar with this particular utility type). But actually your solution helped me develop my own, which makes use of only plain objects. I'll post it in a minute. – Michał Kaczanowicz Aug 24 '19 at 10:56
0

I managed to develop solution using mapped types:

const prop = <K extends string>(key: K) =>
  <T extends { [k in K]: T[K] }>(obj: T): T[K] =>
  obj[key];

Link to Typescript Playground