Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using ReturnType with generic function parameters and generic property access #26856

Closed
blakeembrey opened this issue Sep 3, 2018 · 2 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@blakeembrey
Copy link
Contributor

blakeembrey commented Sep 3, 2018

TypeScript Version: 3.1.0-dev.20180901

Search Terms: ReturnType, generic, invoke, invoker, property

Code

function invoker <K extends string | number | symbol, A extends any[]> (key: K, ...args: A) {
  return <T extends Record<K, (...args: A) => any>> (obj: T): ReturnType<T[K]> => obj[key](...args)
}

const result = invoker('test', true)({ test: (a: boolean) => 123 })

Workaround (duplicate similar ReturnType-like type locally):

export type InvokeResult <T extends (...args: A) => any, A extends any[]> = T extends (...args: A) => infer R ? R : never

export function invoker <K extends string | number | symbol, A extends any[]> (key: K, ...args: A) {
  return <T extends Record<K, (...args: A) => any>> (obj: T): InvokeResult<T[K], A> => obj[key](...args)
}

Expected behavior: Valid program and use of ReturnType.

Actual behavior:

Type '(...args: A) => any' does not satisfy the constraint '(...args: any[]) => any'.
  Types of parameters 'args' and 'args' are incompatible.
    Type 'any[]' is not assignable to type 'A'.

Playground Link: https://www.typescriptlang.org/play/index.html#src=function%20invoker%20%3CK%20extends%20string%20%7C%20number%20%7C%20symbol%2C%20A%20extends%20any%5B%5D%3E%20(key%3A%20K%2C%20...args%3A%20A)%20%7B%0D%0A%20%20return%20%3CT%20extends%20Record%3CK%2C%20(...args%3A%20A)%20%3D%3E%20any%3E%3E%20(obj%3A%20T)%3A%20ReturnType%3CT%5BK%5D%3E%20%3D%3E%20obj%5Bkey%5D(...args)%0D%0A%7D

Related Issues: No, this is out of my skillset to debug tonight. Logging for posterity and help 😄 I did try different workarounds and found I couldn't workaround it with any usage of property access so I added that to the title. For instance, the exact same code inlined results in an empty interface:

export function invoker <K extends string | number | symbol, A extends any[]> (key: K, ...args: A) {
  return <T extends Record<K, (...args: A) => any>> (obj: T): T[K] extends (...args: A) => infer R ? R : never => obj[key](...args)
}

const result = invoker('test', true)({ test: (abc: boolean) => 123 })

More minimal repro which makes me realise it may not be an issue but maybe an enhance to ReturnType to specify the generic type parameters:

type Test <T extends (...args: A) => any, A extends any[]> = ReturnType<T>
@ahejlsberg
Copy link
Member

Yeah, that error is unfortunate. Here's a minimal repro:

type Foo<A extends any[]> = ReturnType<(...args: A) => string>;

In --strictFunctionTypes mode this produces an error similar to the one you're seeing, the issue being that (...args: A) => string does not satisfy the constraint (...args: any[]) => any because any[] is not assignable to A. It seems that we need to be a little more permissive in the way we check assignability of rest parameters with type any[].

@ahejlsberg ahejlsberg self-assigned this Sep 3, 2018
@ahejlsberg ahejlsberg added the Bug A bug in TypeScript label Sep 3, 2018
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.2 milestone Sep 4, 2018
@Voronar
Copy link

Voronar commented Jan 18, 2019

Less safety version of ReturnType but working for all inputs:

type UnsafeReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

Update1
But inference not working properly for all cases.
Cases above working correct.
But not working for union type rest parameters case (with enabled strictFunctionTypes):

const foo = (...args: [string, number] | [1]) => ({
  type: 'type',
  args,
});

type t = UnsafeReturnType <typeof foo>; // any

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

5 participants