Skip to content

Support inferring rest args that are not at the end of the function's args #39595

Closed
@dantman

Description

@dantman

Search Terms

variadic infer rest

Suggestion

TypeScript 4.0 added support for "Variadic Tuple Types", the key relevant change being that spreads for tuple types can be done anywhere in a tuple, not just at the end. However it appears this does not extend to rest and infer.

It should be possible to infer an ...args that is not at the end of a function (callable?) type.

Use Cases

I use TypeScript API clients generated from Swagger/OpenAPI specs in React code by creating hooks to make the API calls in a React safe way. Naturally I want the to pass the API method args through the hook so the input args have the correct type.

However the API methods on these clients typically have something like an options?: FetchOptionsType at the end that shouldn't be part of the input args type. Options like these would be the purview of the hook itself, not relevant to the input arg. (...args: infer A, options? FetchOptionsType) => any seems like it would be the natural way to infer input args without the last arg, but does not work.

Examples

type ApiMethod = (...args: any[]) => Promise<void>;
// This does not work, even in TS 4.0
type ApiMethodArgs<T> = T extends (...args: infer A, options?: any) => any
  ? A
  : never;
// This would work but gives the wrong type
// type ApiMethodArgs<T> = T extends (...args: infer A) => any
//   ? A
//   : never;
// @note The following works so infer is valid when mixing rest/non-rest args but just doesn't work if rest args are not last
// type ApiMethodArgs<T> = T extends (options: any, ...args: infer A) => any
//   ? A
//   : never;

// External API method, all API methods follow 
async function getUser(id: string, options?: any): Promise<void> { }

function callApi<AM extends ApiMethod>(method: AM) {
    const commonApiOptions = {};
    return async (...args: ApiMethodArgs<AM>): Promise<void> => {
        await method(...args, commonApiOptions)
    }
}

callApi(getUser)('asdf');

https://www.typescriptlang.org/play/index.html?ts=4.0.0-beta#code/FAFwngDgpgBAghAlgWSiAFgewCYwLwwAUAdKQIYBOA5gM4BcMZAdmANoC6AlPgHwwAKFTAFtENKAB4AbpkTYeAbmAB6ZTAAq6MTGyYoNGE0wgYAd0wUA1gBoYUKVCYxET9QGUYAFmIAGUJFgEFDQsbDhqGgl1PgJ1OwAPEEdsAxJyCIYXADMoCnhbTAgQREwmGgB+BmYwbjw+auAYGHL4RpgGJntcpVUNLQNzAFcAG1xzKxgAI0GTKkQHAwxYUyEmKhhwaBU1TcCkVAwccNoomI0EpKYUolJiSlpMphy8uFr6lm2m5tbepo6uig9NQAASMST6sCymGGw0wphc63GlgMNEwzieuWcBikZGGcjM6EcMFE8QRMAo+hAyiMTAAtBSaCZ7gZpiYAFaDRk6PQ0JgAchMSOcWXJlMYEXFsDBMGGZEZn128H2ISOEVO+HOUESyVShWKpXojBYtluzMez3gbyNYE+TRacFt7UMAKUnwAotqKExcfB+ABJYkq7C2XHDX0B4RBgxQmFwmDAOVgJgAYxgWUGKf1TioaAAquIKIQ5AxGRQEQUiiUypVrZwGIIRGJJDI5HwAN4wAC+wGA6czVZgydDQQkcGQFx1SuCh3khEjM4YY+4bbaTWTBpM6+EwlKQQA8pWDRq252lF9RSBBl7GDQk6m0ncMlODqFjpExzw6wIhKJxNJZPIvAwCu57nmQphkIgJjzqED7MrYW47kw+6HmUnCrl2wDdsAQ4wkEhA5iA+a5JwhB8nK2BZHynAKEAA

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions