1

I have a function that will always return a type, though it can change and defining it by hand would be some work and not scalable, so I'm trying to achieve this by using typescript's infer keyword

At first I saw this reddit post that uses

type Foo<T> = T extends { a: infer U, b: infer U } ? U : never;
type T10 = Foo<{ a: string, b: string }>;  // string
type T11 = Foo<{ a: string, b: number }>;  // string | number

Problem is, my type is a generic function

Here is one minimal example,

const fnFromLib = <T>(a: string, b: Record<string, string>) => {
  return function barFn(c: T) {
    return {
      c,
      b: b[a],
    };
  };
};
const foo = <T>(a: Record<string, string>) => {
  return {
    bar: fnFromLib<T>('foo', a),
  };
};
type returnTypeInferer<T> = T extends (a: Record<string, string>) => infer U ? U : never;
type fooType = typeof foo;
type fooReturnType = returnTypeInferer<fooType>;

there are no errors, but, since there was not a generic type being passed, fooReturnType will be inferred as

type fooReturnType = {
    bar: (c: unknown) => {
        c: unknown;
        b: string;
    };
}

Noting that T from fnFromLib is not inferred from arguments and should be passed in the function call I try to pass on a type argument from fooReturnType to fooType, but that gives me a parsing error

type returnTypeInferer<T> = T extends (a: Record<string, string>) => infer U ? U : never;
type fooType<T> = typeof foo<T>; // ------   parsing error: ';' expected ---------
type fooReturnType<T> = returnTypeInferer<fooType<T>>;

Is there a way I can achieve what I want?

Thank you

BigFilhao
  • 157
  • 6
  • 15
  • Without something like [generic values](https://github.com/microsoft/TypeScript/issues/17574) in TypeScript, I think you'll have to use workarounds. – jcalz Nov 27 '19 at 16:37

1 Answers1

3

Managed to do it by wrapping the function into a generic class

class GnClass<T> {
  foo = (a: Record<string, string>) => {
    return {
      bar: fnFromLib<T>('foo', a),
    };
  };
}
type returnTypeInferer<T> = T extends (a: Record<string, string>) => infer U ? U : never;
type fooType<T> = GnClass<T>['foo'];
type fooReturnType<T> = returnTypeInferer<fooType<T>>;

But I want to stick with functional programming, I will not mark this as accepted answer, will use it for now as I see no other alternative in the moment and would love another approach.

BigFilhao
  • 157
  • 6
  • 15
  • 1
    I think this is very clever and is probably one of the few ways (or maybe the only way) you can move a generic type parameter off of a function and into a value without wider support for [generic values](https://github.com/microsoft/TypeScript/issues/17574). Also note that TypeScript provides `ReturnType` as a utility type. – jcalz Nov 27 '19 at 16:44
  • Thanks for the utility type tip :) maybe this is the only way, since I'm infering a type from an implemented generic function, so it must exist someway, if it was possible to get the type of the function as `typeof foo` it would be great, but it is not... – BigFilhao Nov 27 '19 at 20:49