Dan Prince
Dan Prince

Reputation: 29989

TypeScript creating a type based on number of parameters

Why does the extends clause succeed when checking whether a 0-arity function extends a 1-arity type?

Is there any way to make the first form work?

The same check on tuples works as I'd expect, so I can use Parameters instead, but I'm curious as to why.

function foo(bar: number) {
  // ...
}

function baz() {
  // ...
}

type Foo = typeof foo extends (bar: infer T) => void ? T : never; // number - expected number
type Baz = typeof baz extends (bar: infer T) => void ? T : never; // unknown - expected never


type Foo2 = Parameters<typeof foo> extends [infer T] ? T : never; // number - ok
type Baz2 = Parameters<typeof baz> extends [infer T] ? T : never; // never - ok

Playground Link

Upvotes: 1

Views: 49

Answers (1)

101arrowz
101arrowz

Reputation: 1905

I find this behavior strange myself, but the issue is that TypeScript says that less parameters is a subtype of more parameters. What this means is that () => void extends (x: number) => void, but not vice versa. TypeScript doesn't know the value of the parameter in type Baz (since there are no parameters in typeof baz) so it says the extends clause is true and sets T to unknown.

On the other hand, tuple types can't really extend each other unless the type in each index of the subtype tuple extend the value of the corresponding index in the supertype tuple (i.e. it's true that [number & string, number] extends [number, number]). That means when there are a different number of parameters, the extends clause always returns false and makes your second example work.

This is definitely pretty confusing, so here's a playground showing the difference.

Upvotes: 1

Related Questions