TypeScript: ReturnType returns unknown if function's return type is generic argument of the function (with default value) that has not been set

πŸ”Ž Search Terms

ReturnType, generic, default

πŸ•— Version & Regression Information

  • This is a crash
  • This changed between versions ______ and _______
  • This changed in commit or PR _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________
  • I was unable to test this on prior versions because _______

⏯ Playground Link

No response

πŸ’» Code

const func = <Return = string>(): Return {
   //
}

type ShouldBeNumber = ReturnType<typeof func<number>>; // number
type ShouldBeString = ReturnType<typeof func>; // unknown

πŸ™ Actual behavior

ReturnType returns unknown.

πŸ™‚ Expected behavior

ReturnType returns default type.

Additional information about the issue

This was encountered when trying to write this kind of function:


const invoker  = <Endpoint extends (...args: any) => any>(endpoint: Endpoint, ...parameters: Parameters<Endpoint>): ReturnType<Endpoint> {
    // Perform some logic before endpoint function is called

    return endpoint(...parameters);
}

About this issue

  • Original URL
  • State: closed
  • Created 4 months ago
  • Comments: 15 (2 by maintainers)

Most upvoted comments

Duplicate of #48870.

If not removing this split competely, some expression that can be used to convert one to another might fix this issue?

Is this what you’re looking for?

const func = <Return,>(): Return => {
  throw new Error("implement me")
}
type Func<T=string,> = typeof func<T>
type ShouldBeNumber = ReturnType<Func<number>>; // number
//   ^?
type ShouldBeString = ReturnType<Func>; // string
//   ^?

https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZgVzMGBeGAeASgUyggJzABoA+ACgEoAuGXfItUmAbwCgYYoALAkAdxhgcggKIE+BcgCIAlgFsADgBsc8nGFjrplNgF82UAJ6KcMAGJJgGACqpoBWWADmZNFxM4QceFdulDTxgAZW4QBGUAEwAhHAA5BHkAIxwCd3pCMBtPDEtkDDBElIJSUgBuGAB6SqEi1LZqzhgAPQB+QNMQsIiYnGCoRxd0vEzs01yrcqqahydnBprONqA

That exhibits the same distinction as

type Fun1<T = string> = () => T;
type Fun2 = <T = string>() => T;

One is a generic type (i.e. a type-function) that resolves to a concrete, non-generic function type. The other is the type of a generic function, whose return type is not known until it’s called.

Note that this is the same reason you can’t write Fun2<number>.

I do not think this is working as indented in sense that it might be an intentional side effect but it is frankly stupid to claim that this is β€œproper” behaviour that should be upheld.

If this is working

type ExtractDefault<T> = T extends infer U ? U : never;
type MyType<T = number> = T;
type DefaultType = ExtractDefault<MyType>; // number, NOT unknown

I see no reason why it should not work with functions.