Home > Net >  Typescript: Array with different type when index is odd or even
Typescript: Array with different type when index is odd or even

Time:12-23

I am looking for a type OddEvenArray<Odd,Even>, which can do the following thing typesafe as OddEvenArray<string,number>: ["a",1,"b",2].

I have no idea to distinguish between odd and even index during typing.

PS: I am sorry, but the JSON I am dealing with looks like that ^^'

CodePudding user response:

An array index is a number type, and number types can either be any number (as number) or specific numbers (as 1 | 2 | 47).

There aren't really any other options at the type level. This means that something like this not possible to annotate as of Typescript 4.9.

CodePudding user response:

You cannot do that with Typescript. I wish you could. It would be a cool language feature.

The closest thing to an "even number" type would look something like the following. Wrap it in a class. Do the same thing with even numbers. Work out the kinks.

class EvenNumber {
  public constructor(n: number) {
    if (n % 2 != 0) {
      throw new Error("argument is not even:", n);
    }
    this.num = n;
  }
}

CodePudding user response:

this is only possible with function generic, you cannot directly assign this type to a variable, you must check with function

type Narrow<T> =
    | (T extends infer U ? U : never)
    | Extract<T, number | string | boolean | bigint | symbol | null | undefined | []>
    | ([T] extends [[]] ? [] : { [K in keyof T]: Narrow<T[K]> });

type Odd<
    X extends number,
    Y extends unknown[] = [1],
    Z extends number = never
> = Y['length'] extends X
    ? Z | Y['length']
    : Odd<X, [1, 1, ...Y], Z | Y['length']>

type OddNumbers = Odd<1999> // 1 | 3 | 5 | 7 | ....1997

type IsStringAndOddTuple<T extends unknown[], ACC extends any[]=[]> = 
    T extends [infer A, ...infer J] 
        ? T['length'] extends OddNumbers 
            ? IsStringAndOddTuple<J,[...ACC, A extends number? A : "expect number type at even index" ]>
            : IsStringAndOddTuple<J,[...ACC, A extends string? A : "expect string type at Odd index" ]>
        :ACC

const isStringAndOddTuple =<T extends readonly unknown[]>(tuple: T extends never? T : Narrow<T> extends unknown[] ? IsStringAndOddTuple<Narrow<T>>:never)=>{
  // don't neeed anything here
}

type C = IsStringAndOddTuple<[1, 2, 3, 4]>
//   ^?

type D = IsStringAndOddTuple<["a","b","c","d"]>
//   ^?

type E = IsStringAndOddTuple<["a",1,"b",2]>
//   ^?

isStringAndOddTuple([1, 2, 3, 4]) 
isStringAndOddTuple(["a","b","c","d"]) 
isStringAndOddTuple<["a",1,"b",2]> 

enter image description here

playground

the problem with this method is, it does not support tuple length over 1000 because IsStringAndOddTuple max recursion is only 1000

I believe there is a meta solution that exceed this limitation, but the works are just too much

even so you tuple length cannot exceed 10,000 because max length of a tuple is 10,000

there is another more scalable method, but that requires you to rebuild the tuple

reference:
Odd Number Type
Generic Type Narrowing <-- see the 2nd comment

  • Related