Gorbit99
Gorbit99

Reputation: 1

Typescript length check doesn't constrain tuple type

I have a function that takes in a parameter of type [unknown, ...unknown], that is given by a template. I want to call this function recursively, for each element in that tuple, until none are left, at which point it should stop. I tried the following:

type Tail<T extends [...unknown[]]> = 
  T extends [unknown, ...infer Tail]
    ? Tail
    : never;

function foo<T extends [unknown, ...unknown[]]>(value: T) {
  ...
  const [head, ...tail] = value;

  if (value.length === 0) {
      return;
  }
  foo<Tail<T>>(tail);
}

But I'm getting the error "Source provides no match for required element at position 0 in target".

I've tried creating a conditional type that returned never for empty tuples, but because I'm using the template for other parameters, it wouldn't allow that either, because then those parameters weren't compatible.

I've also tried having T only extend [...unknown[]], but then that shifted that issue to the const [head, ...tail] line, as then I don't necessarily have a 0th element and checking for length there doesn't work either.

Even tried using the check 0 in tail and then typeof tail to no avail.

Is there a way to restrict this type so it knows that it has at least one element there, or another way of writing this?

Upvotes: 0

Views: 285

Answers (1)

Edwin
Edwin

Reputation: 415

Here is the example of how Tail works on TypeScript Playground

type Tail<V> = V extends readonly [] ? V : V extends readonly [any?, ...infer VTail] ? VTail : V;

function foo<T extends unknown[]>(value: T) {
  const [head, ...tail] = value;
  console.log(`head:`, head);

  // stop when no tail found
  if (tail.length === 0) {
    return;
  }

  // TypeScript can resolve `tail` by itself
  foo(tail);
}

foo([1, "2", "3", 4]);

Upvotes: 0

Related Questions