Home > Software engineering >  Arguments type constaint in Typescript function
Arguments type constaint in Typescript function

Time:03-06

I'm struggling on this for a long time,

function processEntity<
  T extends Record<string, unknown>,
  U extends keyof T = keyof T,
  V extends Pick<T, U> = Pick<T, U>
>(arg: {keys: Array<U>; resolver: (doc: V) => void}) {
  arg.resolver(...)
}

type Cat = {
  name: string
  age: number
}

processEntity<Cat>({
  keys: ['name'],
  resolver: (doc) => {
    // doc.name should exists
    // doc.age should be undefined
  },
})

As you can see above, there is a function processEntity which accepts an object as the argument.

What I want is to make type of argument of arg.resolver depends on type of arg.keys. Say if I pass ['name'] to keys: field, then when doc.age should not be accessable because 'age' was missed in keys field.

I've looked at some questions listed below but they does not help, so please help :pray:

CodePudding user response:

You are very close. The main problem is that Typescript does not support partial inference in functions. This means you can either specify all type parameters, or you can let typescript infer all of them, but you can't specify some and let TS infer the others. This means you can't specify T and let TS infer U.

There can be several solutions to this. One is to use function currying to specify T in a first call and let TS infer U in the second call:

function processEntity<
    T extends Record<string, unknown>>() {
    return function <
        U extends keyof T = keyof T
    >(arg: { keys: Array<U>; resolver: (doc: Pick<T, U>) => void }) {
        //   arg.resolver(...)
    }
}

Playground Link

Or you could add a dummy field to have an inference location for T, the value of which does not really matter:

function processEntity<
    T extends Record<string, unknown>,
    U extends keyof T = keyof T,
    >(arg: { type: T, keys: Array<U>; resolver: (doc: Pick<T, U>) => void }) {
    //arg.resolver(...)
}

Playground Link

  • Related