我想为我的TypeScrip类实现[Symbol.iterator],它允许我进行强类型的元组解构.

例如,这个类在内部存储了一个强类型的元组,我通过直接访问成员成功地对其进行了分解:

class Deserializer<ParsedArgs extends Array<unknown> = []> {
  deserialized: ParsedArgs;
  // ...
}

const [str, vec] = (new Deserialized(/* ... */)).deserialized

我想添加一个[Symbol.iterator]实现,这样我就可以go 掉.deserialized.我实际上可能不会这样做,因为这是一个相当出人意料的API,但在TypeScrip中这可能吗?

我试过了,但各个组件最终都输入了string | Vector3,原因很明显:

  [Symbol.iterator](): Iterator<ParsedArgs[number]> {
    let index = 0;
    const data = this.deserialized;

    return {
      next: function () {
        return { value: data[++index], done: !(index in data) }
      }
    };
  }

Typescript playground link for below full test code

type Vector3 = [x: number, y: number, z: number];
type DeserializeResult<T> = [deserialized: T, newOffset: number];

// Deserialize a length-prefixed string
function deserializeString(
  args: ArrayBuffer,
  offset: number
): DeserializeResult<string> {
  const len = new Uint32Array(args.slice(offset, offset + 4))[0];
  const str = new TextDecoder().decode(
    args.slice(offset + 4, offset + 4 + len)
  );
  return [str, offset + 4 + len];
}

// Deserialize three floats in a row
function deserializeVector3(
  args: ArrayBuffer,
  offset: number
): DeserializeResult<Vector3> {
  const floatArr = new Float32Array(args.slice(offset, offset + 3 * 4));
  const result: Vector3 = [floatArr[0], floatArr[1], floatArr[2]];
  return [result, offset + 3 * 4];
}

class Deserializer<ParsedArgs extends Array<unknown> = []> {
  buffer: ArrayBuffer;
  offset: number;
  deserialized: ParsedArgs;

  constructor(buffer: ArrayBuffer, deserialized: ParsedArgs, offset: number) {
    this.deserialized = deserialized;
    this.buffer = buffer;
    this.offset = offset;
  }

  private peel<T>(
    deserializerFunc: (
      buffer: ArrayBuffer,
      offset: number
    ) => DeserializeResult<T>
  ) {
    const [value, offset] = deserializerFunc(this.buffer, this.offset);
    this.offset = offset;
    this.deserialized.push(value);
    return this as unknown as Deserializer<[...ParsedArgs, T]>;
  }

  Vector3() {
    return this.peel(deserializeVector3);
  }

  string() {
    return this.peel(deserializeString);
  }
}

function deserialize(buffer: ArrayBuffer): Deserializer<[]> {
  return new Deserializer(buffer, [], 0);
}

async function test() {
  const buffer = await (new Blob([
    // str1
    new Uint32Array([3]),
    "abc",

    // vec1
    new Float32Array([123.456, 7, 8]),

    // str2
    new Uint32Array([5]),
    "hello",

    // vec2
    new Float32Array([-1, -2, -3]),
  ]).arrayBuffer())


  const [str1, vec1, str2, vec2] = deserialize(buffer)
    .string()
    .Vector3()
    .string()
    .Vector3().deserialized;

  // assertions
  function check<T extends string | Vector3>(t: T) {
    console.log(t)
  }

  check<string>(str1)
  check<Vector3>(vec1)
  check<string>(str2)
  check<Vector3>(vec2)
}

test()

推荐答案

这在Typescript 中目前是不可能的.在microsoft/TypeScript#42033处存在开放特征请求以支持对应于tuple types(固定长度、不同类型等)的表达generators and iterators,而不是仅仅开放的相同元素array types.microsoft/TypeScript#43150号也有类似的公开申请.这两个问题(截至2023-11-14)都被标记为"等待更多反馈",所以如果你想看到这种情况发生,最好go 那里,给他们一个👍,并解释你的用例,如果它比现有的更有说服力的话.它可能也不会太多,但如果有足够多的人这样做,那么它得到实施的可能性就更大.

Typescript相关问答推荐

替换类型脚本类型中的多个特定字符串

TypeScript实用程序类型—至少需要一个属性的接口,某些指定属性除外

使某些类型的字段变为可选字段'

`((PrevState:NULL)=>;NULL)|null`是什么意思?

数组文字中对象的从键开始枚举

具有动态键的泛型类型

使用axios在ngOnInit中初始化列表

如何在TypeScript中将一个元组映射(转换)到另一个元组?

无法从Chrome清除还原的初始状态数据

如何在Angular 12中创建DisplayBlock组件?

缩小并集子键后重新构造类型

Angular文件上传到Spring Boot失败,多部分边界拒绝

将对象的属性转换为正确类型和位置的函数参数

重写返回任何

替换typescript错误;X类型的表达式可以';t用于索引类型Y;带有未定义

Select 类型的子项

在Vite中将SVG导入为ReactComponent-不明确的间接导出:ReactComponent

Typescript泛型-键入对象时保持推理

如何让Record中的每个值都有独立的类型?

使用嵌套属性和动态执行时,Typescript 给出交集而不是并集