Reputation: 8542
Suppose I have an object with properties of different types. Is there a way in TypeScript to have a generic function that performs an operation on a given key and value of an object?
Here's an example. I have an object s
with three fields of different types:
interface Struct {
a: string
b: boolean
c: string
}
const s: Struct = {
a: 'hello',
b: false,
c: 'world',
}
Suppose I have a function that does something with one of the properties:
function makeSetter<K, V>(name: K): (value: V) => void {
return (value) => s[name] = value;
}
How should I parametrize types K
and V
so that for makeSetter('a')
I would get a function of type (value: string) => void
and for makeSetter('b')
I would receive a function of type (value: boolean) => void
?
Upvotes: 2
Views: 73
Reputation: 141652
The approach by Gerrit0
is useful. Here is a second useful approach (that is also in the playground).
function makeSetter<K extends keyof Struct>(name: K): (value: Struct[K]) => void {
return value => (s[name] = value);
}
const setter1 = makeSetter("a")("foo");
const setter2 = makeSetter("b")("foo"); // error
const setter3 = makeSetter("c")(false);
keyof T
is the index type query operator and T[K]
is the indexed access operator. Both are described under the heading Index Types in the Advanced Types docs.
Upvotes: 0
Reputation: 9242
The trick here is to use generic constraints. K
must be a keyof Struct
and V
must be assignable to Struct[K]
. TypeScript docs
interface Struct {
a: string
b: boolean
c: string
}
const s: Struct = {
a: 'hello',
b: false,
c: 'world',
}
function makeSetter<K extends keyof Struct, V extends Struct[K]>(name: K): (value: V) => void {
return (value) => s[name] = value;
}
// Helpers for type "unit tests"
type Equal<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false
function assertTrue<T extends true>() {}
const a = makeSetter('a')
const b = makeSetter('b')
assertTrue<Equal<typeof a, (value: string) => void>>() // No error
assertTrue<Equal<typeof b, (value: boolean) => void>>() // No error
Upvotes: 2