'Is there a way to pass one generic and infer another?

consider the following code:

const selectFrom =
  <T>() =>
  <U extends Partial<T>>(fields: U) => {
    return fields as U;
  };

type Item = { a: string; b: string };

// intellisense on the argument Partial<Item>
const x = selectFrom<Item>()({ a: "" });
// typeof x => { a: string }

I'm able to have the types I want only if selectFrom returns another function. Is there a way to achieve the below target signature?

// target signature:
const x = selectFrom<Item>({ a: "" });

// current signature:
const x = selectFrom<Item>()({ a: "" });


Solution 1:[1]

What you want to achieve is currently not possible (kind of possible, see below).

However, if you are willing to bear with an extra variable for each of the instantiations of T, you can separate the function signature from the implementation and move the T type parameter to be a type alias's required type parameter while keeping the rest of the signature generic and constrained to Partial<T>:

type selectFrom<T> = <U extends Partial<T>>(fields: U) => U;

type Item = { a: string; b: string };

type Item2 = { answer: 42, question: unknown };

const selectFromI: selectFrom<Item> = (fields) => fields;
const selectFromJ: selectFrom<Item2> = (fields) => fields;

// intellisense on the argument Partial<Item>
const y = selectFromI({ a: "" });
//    ^? const y: { a: string; }
const x = selectFromJ({ answer: 42 });
//    ^? const x: { answer: 42; }

It is not ideal, but at least you do not need to make an extra "hollow" call every time you want to use the selectFrom function (and you are likely to find yourself wanting to extract the first call into a variable anyways).

On a related note, TypeScript 4.7 is getting instantiation expressions that, although unfortunately do not allow for your use case, seem to be a step in the direction of allowing partial application of generic type parameters.

Playground

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Oleg Valter is with Ukraine