Typescript:我可以定义一个n长度的元组类型吗?

时间:2018-09-25 01:12:30

标签: typescript

我正在使用Typescript创建将棋游戏板。将棋板有9个等级和文件。

我想声明一个9x9多维数组作为一种类型,以确保数组的大小和内容。

目前,我正在以这种方式创建9x9木板类型:

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;
}

问题:是否有一种不太繁琐,更通用的方式来定义我称为Board9x9<P>的元组类型?

5 个答案:

答案 0 :(得分:17)

打字稿3引入了rest elements in tuple types

  

元组类型的最后一个元素可以是... X形式的rest元素,其中X是数组类型

要限制元组的长度,我们可以使用与{ length: N }的交集

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

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

这在初始化Tuple类型的变量时起作用:

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

在此警告,如果您尝试访问超出元组范围的索引中的非元素,则打字稿不会警告您:

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'

有一种suggestion添加了一种通用方法来指定元组类型的长度。

答案 1 :(得分:5)

一个快速的简化方法是创建一个Tuple9类型,该类型可用于创建矩阵的第一级和第二级:

type Tuple9<T> = [T, T, T, T, T, T, T, T, T]
type Board9x9<P> = Tuple9<Tuple9<P>>

答案 2 :(得分:1)

借助元组类型别名,您可以制作任意 NxN 板:

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

因此,在您的情况下,您可以执行以下操作:

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>;

答案 3 :(得分:0)

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

答案 4 :(得分:0)

对常见用例的想法(如在这个问题中),其中您尝试创建的类型不应具有会改变底层数组的不安全数组方法(如 pushpop、等):

const board: Tuple<string, 4> = ["a", "b", "c", "d"];
board.pop()
const fourthElement: string = board[3]; // No TS error
fourthElement.toUpperCase() // Errors when run, but no TS error

不要使用元组,而是考虑使用仅限于某些索引的索引签名:

// type BoardIndicies = 0 | 3 | 1 | 2
type BoardIndicies = Partial<Tuple<never, 3>>['length']

const board: Record<BoardIndicies, string> = ["a", "b", "c", "d"];
board.pop() // ERROR: Property 'pop' does not exist on type 'Record<0 | 3 | 1 | 2, string>'.