const getProperty = <P extends string>(prop: P) => <O extends any>(obj: O) => obj[prop]
const record = { id: 4, label: 'hello' }
const getId = getProperty('id')
const id = getId(record)
This seems to work. The type for id
is inferred properly as a number. Only thing is you'll receive any
if the object passed into getId
doesn't have an id
property on it, so it's not strict, but an overall elegant solution.
EDIT: Since writing this answer, I've learned that the Record
type can be used to specify a type of object which requires a specific key. Using this knowledge, we can write a typesafe, succinct, readable solution:
// implementation
const get = <K extends string>(key: K) => <V>(obj: Record<K, V>) => obj[key]
// usage
const person = {
name: "kingdaro",
age: 21,
}
const fruit = {
type: "apple",
color: "red",
}
const nameGetter = get("name")
nameGetter(person) // return type inferred as string
nameGetter(fruit) // fails, fruit has no key "name"
// minor caveat: when passing an object literal, the extra key will raise an error
// you can declare the object separately to sidestep around this
// but this wouldn't come up often anyway
nameGetter({ name: "kingdaro", age: 21 })