Skip to content

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

Closed

Description

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>

Activity

  1. ahejlsberg commented on Sep 3, 2018

    @ahejlsberg
    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[].

  2. Voronar commented on Jan 18, 2019

    @Voronar

    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
  3. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

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