Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: Can I define an n-length tuple type?

Tags:

typescript

I am creating a shogi game board using Typescript. A shogi board has 9 ranks and files.

I'd like to assert a 9x9 multidimensional array as a type to ensure both the size and contents of the array.

Currently I am creating my 9x9 board type this way:

type Board9x9<P> = [
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P],
  [P, P, P, P, P, P, P, P, P]
];

interface IShogiBoardInternalState {
    board: Board9x9<IShogiPiece>;
    playerName: string;
    isYourTurn: boolean;
}

Question: Is there a less tedious, more generic way to define this tuple type which I have called Board9x9<P>?

like image 600
Lucas Avatar asked Sep 25 '18 01:09

Lucas


People also ask

Are tuples fixed in length?

A list has a variable size while a tuple has a fixed size.

What is tuple type in TypeScript?

A tuple is a TypeScript type that works like an array with some special considerations: The number of elements of the array is fixed. The type of the elements is known. The type of the elements of the array need not be the same.

What is the difference between a tuple and an array in TypeScript?

The structure of the tuple needs to stay the same (a string followed by a number), whereas the array can have any combination of the two types specified (this can be extended to as many types as is required).

When should I use tuple TypeScript?

TypeScript tuples are like arrays with a fixed number of elements. They provide us with a fixed size container that can store values of multiple types, where the order and structure are very important. This data type is best used when we know exactly how many types we want to allow in an array.


4 Answers

One quick simplification would be to create a Tuple9 type, that can be used to create the first level as well as the second level of the matrix:

type Tuple9<T> = [T, T, T, T, T, T, T, T, T]
type Board9x9<P> = Tuple9<Tuple9<P>>
like image 33
Titian Cernicova-Dragomir Avatar answered Oct 20 '22 04:10

Titian Cernicova-Dragomir


Update:

With Recursive conditional types (added in TypeScript 4.1.0) it is possible to:

type Tuple<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;

type Tuple9<T> = Tuple<T, 9>;
type Board9x9<P> = Tuple9<Tuple9<P>>;

Playground



Original answer:

Typescript 3 introduces rest elements in tuple types

The last element of a tuple type can be a rest element of the form ...X, where X is an array type

To restrict the length of a tuple we can use intersection with { length: N }

type Tuple<TItem, TLength extends number> = [TItem, ...TItem[]] & { length: TLength };

type Tuple9<T> = Tuple<T, 9>;
type Board9x9<P> = Tuple9<Tuple9<P>>;

This works when variable of Tuple type is being initialized:

const t: Tuple<number, 1> = [1, 1] // error: 'length' incompatible.

A caveat here, typescript won't warn you if you'll try to access non element at index out of tuple range:

declare const customTuple: Tuple<number, 1>;
customTuple[10] // no error here unfortunately

declare const builtinTuple: [number];
builtinTuple[10] // error: has no element at index '10'

There's a suggestion to add a generic way to specify length of a tuple type.

like image 136
Aleksey L. Avatar answered Oct 20 '22 03:10

Aleksey L.


type PushFront<TailT extends any[], FrontT> = (
  ((front : FrontT, ...rest : TailT) => any) extends ((...tuple : infer TupleT) => any) ?
  TupleT :
  never
)

type Tuple<ElementT, LengthT extends number, OutputT extends any[] = []> = {
  0 : OutputT,
  1 : Tuple<ElementT, LengthT, PushFront<OutputT, ElementT>>
}[
  OutputT["length"] extends LengthT ?
  0 :
  1
]

//type t3 = [string, string, string]
type t3 = Tuple<string, 3>
//type length = 0 | 3 | 1 | 2
type length = Partial<Tuple<any, 3>>['length']

Add a generic way to specify length of a tuple type #issuecomment-513116547

like image 32
Deadalus _ Avatar answered Oct 20 '22 03:10

Deadalus _


You can make an arbitrary NxN board with the help of a Tuple type alias:

type Tuple<T, N extends number, A extends any[] = []> = A extends { length: N } ? A : Tuple<T, N, [...A, T]>;

So in your case you'd do something like:

type Tuple<T, N extends number, A extends any[] = []> = A extends { length: N } ? A : Tuple<T, N, [...A, T]>;

type Board9x9<P> = Tuple<Tuple<P, 9>, 9>;
like image 24
sstur Avatar answered Oct 20 '22 03:10

sstur