Reputation: 1300
I have a function that looks like this:
function create<A extends
Record<string, (r:{[k in keyof A]: ReturnType<A[k]>}) => any>> //the Record Value is a function that takes the keys and return values of A as it's argument
(options:A) : any {
...
}
so A is a Record with values that are functions that I would like to take the record keys with the return types for those keys as the values. Something like:
create({
test: () => "TEST",
lower: (r) => r.test.toLowerCase() //r is any here, I want r to have the keys of this record with the values being the Return types of these values
}
Is there a way to do this while getting typings for the other values in the same Record?
Upvotes: 0
Views: 1937
Reputation: 4381
I'm not sure that I catch idea:
type OptionsObject<Map> = {
[K in keyof Map]: ((K?: any) => Map[K]);
}
function create<Map>(options: any): OptionsObject<Map> {
return options;
}
const opt = {
test: () => "TeSt",
lower: (r: any) => r.test(r).toLowerCase(),
upper: (r: any) => r.test(r).toUpperCase()
}
const a = create<typeof opt>(opt)
console.log('a.test()', a.test()) // "a.test()", "TeSt"
console.log('a.lower(a)', a.lower(a)) // "a.lower(a)", "test"
console.log('a.upper(a)', a.upper(a)) // "a.upper(a)", "TEST"
Upvotes: 1
Reputation: 42188
If you define your options
as Record<string, ...>
, that means that every string
is a valid key of options
. So you would not be able to distinguish between valid and invalid keys.
One solution is to use the generic to describe the keys of our mapper. This is not perfect -- we know that r.test
exists and is a function, but we don't know the return type and we don't know that it doesn't actually use the argument so we have to pass r
to call it.
type OptionsObject<Keys extends PropertyKey> = {
[K in Keys]: (r: OptionsObject<Keys>) => any;
}
function create<Keys extends PropertyKey>
(options: OptionsObject<Keys>) : any {
}
create({
test: () => "TEST",
lower: (r) => r.test(r).toLowerCase()
})
We can get a bit better by using the generic to describe a Map
object of the keys and return types. This is able to recognize that test
returns string
, but it can't figure out lower
and it infers the return type as unknown
.
type OptionsObject<Map> = {
[K in keyof Map]: (r: OptionsObject<Map>) => Map[K];
}
function create<Map extends {}>
(options: OptionsObject<Map>) : any {
}
create({
test: () => "TEST",
lower: (r) => r.test(r).toLowerCase()
})
edit: I think I misunderstood this bit "the Record Value is a function that takes the keys and return values of A as it's argument" and what you want is (r: Map) =>
instead of (r: OptionsObject<Map>) =>
.
Upvotes: 1