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>
?
A list has a variable size while a tuple has a fixed size.
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.
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).
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.
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>>
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.
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
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>;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With