Peter Hudec
Peter Hudec

Reputation: 2483

How to have required and optional type parameters in a function and still have working type inference for recod lookup?

How to make a required type parameter Y in a function work together with another type parameter K where the latter is only used as a key in a record R = {a: 'aaa', b: 'bbb'} lookup and is not supposed to be explicitly provided? The type parameter K should be inferred from the value of the first parameter k of the funciton foo. I want then to use K to lookup a type in R so that I can use it for the other params of the function.

To allow for only the first type parameter Y to be required, the K param needs to have a default value. But this default value then breaks the inference of R[K] when instead of inferring based on the value of k i.e. R[typeof k] to "aaa" if k was "a", it uses the default of K i.e. R[keyof R] resolving to a union of all types in the record R i.e. "aaa" | "bbb" disregarding k.

Is it possible to have type inferrence for y: R[keyof K] where K is resolved from the value of k without explicitly passing K, but still allow for the required type param Y?

type R = {
  a: 'aaa',
  b: 'bbb',
}

type Foo = <
  Y,
  K extends keyof R = keyof R,
>(k: K, f: (x: R[K], y: Y) => any) => any

const foo: Foo = (x, y) => null

// With no type params Rec[K] is correctly inferred from K: 'a'
foo('a', (x, y) => null) // x: "aaa"; y: unknown

// When passing the Y type param, Rec[K] ignores the K: 'a'
foo<number>('a', (x, y) => null) // x: "aaa" | "bbb"; y: number

// When passing both Y and K it works
foo<number, 'a'>('a', (x, y) => null) // x: "aaa"; y: number

I could only make it work by currying the function so each type parameter belongs to a separate function:

type FooCurried = <K extends keyof R = keyof R>(k: K) =>
  <Y>(f: (x: R[K], y: Y) => any) =>
    any

const fooCurried: FooCurried = x => y => null

fooCurried('a')<number>((x, y) => null) // x: "aaa"; y: number

Upvotes: 0

Views: 348

Answers (1)

Peter Hudec
Peter Hudec

Reputation: 2483

Found a fairly similar question where the accepted answer says that this can only be done by currying the function, as I did in the example.

Upvotes: 0

Related Questions