Skip to content

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

@blakeembrey

Description

@blakeembrey

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>

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions