The Workaround

Using TypeScript 3.0 it is now possible to tackle that problem. The fix itself looks no better than a dirty hack to overcome and simulate desired behavior, but hey, it works, and after all how many //HACK: and //TODO: can you find in your project already?

Straight to the code. Consider simple problematic example:

There’s a new cool typing pattern, called rest parameters with tuple types . In docs it is explained like this:

When a rest parameter has a tuple type, the tuple type is expanded into a sequence of discrete parameters.

For example the following two declarations are equivalent:

function foo(…args: [number, string, boolean]): void

function foo(args_0: number, args_1: string, args_2: boolean): void

However, if we set the type of rest parameter as empty tuple ( [] ), it is expanded to 0 arguments! Following two are equivalent as well:

function foo(…args: []): void

function foo(): void

We can abuse that behavior to achieve our desired workaround. Implementation:

Let me explain what changed and why it works.

First of all, add default generic type by changing function foo<T> to function foo<T = undefined> , so that if no type is provided, it will default to undefined .

Second, introduce new “wrapper” type. I’ve called it OptionalSpread , but it may also be called something like ‘AllowMissing’, ‘WithOptional’, etc. It looks like this:

type OptionalSpread<T> =

T extends undefined

? []

: [T]

This type takes “wrapped” type and checks if it is undefined . If it is, it returns empty tuple type ( [] ), if it is not, it returns this type wrapped into a tuple ( [T] ). number will be wrapped into [number] type, and undefined into [] .

Third, change function signature from (arg: T) to (…args: OptionalSpread<T>) to accept spread of parameters of type OptionalSpread<T> instead of single parameter. Now, if generic type T is undefined , ...args will have an empty tuple type - [] , and will be expanded to an empty parameters list. Else if T is not an undefined , ...args will have a type of tuple [T] , and will be expanded to a single parameter of type T .