TypeScript:如何在编译时声明固定大小的数组以进行类型检查

ben*_*inz 9 javascript typechecking typescript

更新:这些检查用于编译时,而不是在运行时.在我的例子中,失败的案例都是在编译时捕获的,我期待其他应该失败的情况下的类似行为.

假设我正在编写一个类似于表的类,我希望该类的所有成员都是相同长度的数组,如:

class MyClass {
  tableHead:  string[3]; // expect to be a 3 element array of strings
  tableCells: number[3]; // expect to be a 3 element array of numbers
}
Run Code Online (Sandbox Code Playgroud)

到目前为止我发现的最接近的解决方案是:

class MyClass {
  tableHead:  [string, string, string];
  tableCells: [number, number, number];
}

let bar = new MyClass();
bar.tableHead = ['a', 'b', 'c']; // pass
bar.tableHead = ['a', 'b'];      // fail
bar.tableHead = ['a', 'b', 1];   // fail

// BUT these also pass, which are expected to fail at compile time
bar.tableHead = ['a', 'b', 'c', 'd', 'e']; // pass
bar.push('d'); // pass
bar.push('e'); // pass
Run Code Online (Sandbox Code Playgroud)

有更好的想法吗?

Huy*_*yen 8

更新2:从3.4版开始,OP所要求的现在完全可以使用简洁的语法实现(Playground链接):

class MyClass {
  tableHead: readonly [string, string, string]
  tableCells: readonly [number, number, number]
}
Run Code Online (Sandbox Code Playgroud)

更新1:从2.7版开始,TypeScript现在可以区分大小不同的列表

我认为不可能对元组的长度进行类型检查。是TypeScript的作者对此主题的看法。

我认为您的要求不是必需的。假设您定义此类型

type StringTriplet = [string, string, string]
Run Code Online (Sandbox Code Playgroud)

并定义该类型的变量:

const a: StringTriplet = ['a', 'b', 'c']
Run Code Online (Sandbox Code Playgroud)

您无法从该三元组中获取更多变量,例如

const [one, two, three, four] = a;
Run Code Online (Sandbox Code Playgroud)

将给出一个错误,而这与预期的不同:

const [one, two, three] = a;
Run Code Online (Sandbox Code Playgroud)

我认为缺乏约束长度的能力的唯一情况就是例如当您map超过三重奏时

const result = a.map(/* some pure function */)
Run Code Online (Sandbox Code Playgroud)

并期望result有3个元素,而实际上可以有3个以上。但是,在这种情况下,您还是将其a视为集合而不是元组,因此这不是元组语法的正确用例。


can*_*ero 5

来自打字稿:我可以定义一个长度为 n 的元组类型吗?,以编程方式,具有动态长度:

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

type Tuple9<T> = Tuple<T, 9>;
Run Code Online (Sandbox Code Playgroud)


Chr*_*ins -1

这是一个控制其内部数组长度的类的简单示例。它并不是万无一失的(当获取/设置时,您可能需要考虑您是否是浅/深克隆等:

https://jsfiddle.net/904d9jhc/

class ControlledArray {

  constructor(num) {
    this.a = Array(num).fill(0); // Creates new array and fills it with zeros
  }

  set(arr) {
    if (!(arr instanceof Array) || arr.length != this.a.length) {
      return false;
    }
    this.a = arr.slice();
    return true;
  }

  get() {
    return this.a.slice();
  }

}

$( document ).ready(function($) {

  var m = new ControlledArray(3);

  alert(m.set('vera')); // fail
  alert(m.set(['vera', 'chuck', 'dave'])); // pass

  alert(m.get()); // gets copy of controlled array

});
Run Code Online (Sandbox Code Playgroud)