0

So, here are the resources I am basing my expectations on:

https://github.com/Microsoft/TypeScript/pull/24897 (Rest parameter with tuple behavior) https://www.typescriptlang.org/docs/handbook/type-compatibility.html (Type compatibility)

interface Theme {
  primary: string;
  secondary: string;
  tertiary: string;
}

type Color<ExtraArgType = void> = (
    theme: Theme,
    ...other: ExtraArgType extends void ? [undefined?] : [ExtraArgType]
  ) => string;

type ButtonMode = 'contained' | 'outlined' | 'text';

// TS does not allow this
const foo: Color<ButtonMode> = (theme) => theme.primary;

// Allowed, but need to include unused argument
const bar: Color<ButtonMode> = (theme, _) => theme.primary;

// What I want to be allowed
const baz: Color<ButtonMode> = (theme) => theme.primary;
const qux: Color<ButtonMode> = (theme, mode) => mode === 'contained' ? theme.primary : theme.secondary;
const foobar: Color = (theme) => theme.primary;

Note, this is not an issue when I use a non-union type such as Color<string>.

How do I avoid having the extra unused argument?

  • Is your goal that the `foo` be allowed and `bar` also allowed? The criteria aren't really clarified here. Telling us what happens != what you want to happen. – kelsny Sep 21 '22 at 14:44
  • @caTS Yes, both should be allowed. I want to avoid having the extra unused argument if the function does not need to depend on it. I will include an example. – lejevap290 Sep 21 '22 at 15:00
  • 1
    Ah, so you are looking for something like [this](https://tsplay.dev/WyXY2m)? – kelsny Sep 21 '22 at 15:01
  • @caTS Wow, that looks like exactly what I want. Still need to make sure it works in my actual TS code, but it works as intended in TS playground. What difference do the extra brackets provide? You can create an answer so I can accept. – lejevap290 Sep 21 '22 at 15:06

1 Answers1

0

It's actually a simple but unintuitive solution:

type Color<ExtraArgType = void> = (
  theme: Theme,
  ...other: [ExtraArgType] extends [void] ? [undefined?] : [ExtraArgType]
) => string;

This is to prevent the distributive nature of conditional types, as outlined in the last example of the documentation on conditional types. See this question for a more detailed response about this.

In the future, I suggest using never instead of void.

Playground

kelsny
  • 23,009
  • 3
  • 19
  • 48