键入一个函数,它执行 return 相同长度的数组

Type a function as such it does return an array of same length

有没有一种方法可以键入一个函数,这样它就说“我采用泛型类型的数组,并将 return 具有相同长度的相同类型”

interface FixedLengthArray<T, L extends number> extends Array<T> {
  length: L;
}

export function shuffle<T, Arr extends Array<T>>(array: Arr):FixedLengthArray<T, Arr['length']> {
  const copy: T[] = [...array];
  for (let i = array.length - 1; i > 0; i -= 1) {
    const j = Math.floor(Math.random() * (i + 1));
    [copy[i], copy[j]] = [copy[j], copy[i]];
  }

  return copy;
}

function isPairComplete<T>(arr: T[]): arr is [T, T] {
  return arr.length === 2
}

const checkThis: (pair: [number, number]) => void = ([one, two]) => {
  //
}

checkThis(shuffle([1, 2]))

这是一个link to Typescript playground

我还在怀疑自己是不是在用好东西。也许解决方案是重复使用相同的类型 FixedLengthArray 来键入我的函数 checkThis

虽然您可以尝试通过提供数字 literal type to its length property, the compiler won't really be able to do much with it. It is probably better to just use tuple types 来描述您自己的 fixed-length 数组类型,但它已经表示 fixed-length 数组类型。

如果 shuffle() 取某个 array/tuple 类型的值 T 并生成一个包含随机顺序的相同元素的新数组,那么我们可以用一种方法来描述 return类型是这样的:

type Shuffled<T extends any[]> = { [I in keyof T]: T[number] };

当您制作任何 element-specific 类型的 mapped type over array/tuple types, the result is another array/tuple type of the same length. And in this instance we're saying that each element of the resulting tuple will have the type of some element from the initial tuple. The type T[number] means "the type of the value stored at a number index of the T type", which will end up being the union 时。所以如果你传递一个 heterogeneous tuple like [string, number] ,你会得到一个 homogeneous tuple like [string | number, string | number]出来。

让我们测试一下它的行为:

type Test1 = Shuffled<[number, number, number]>;
// type Test1 = [number, number, number]

type Test2 = Shuffled<[string, number, string]>;
// type Test2 = [string | number, string | number, string | number]

type Test3 = Shuffled<Date[]>
// type Test3 = Date[]

看起来不错。请注意,对于 Test3,像 Date[] 这样的 non-tuple 数组保持不变;如果编译器不知道输入长度,输出长度也是未知的。


现在我们可以为您的 shuffle() 实现提供更合适的输入:

function shuffle<T extends any[]>(array: [...T]) {
  const copy = [...array];
  for (let i = array.length - 1; i > 0; i -= 1) {
    const j = Math.floor(Math.random() * (i + 1));
    [copy[i], copy[j]] = [copy[j], copy[i]];
  }
  return copy as Shuffled<T>;
}

const shuffled = shuffle([1, 2, 3]);
// const shuffled: [number, number, number]

const alsoShuffled = shuffle([1, "", false]);
// const alsoShuffled: [string | number | boolean, 
//   string | number | boolean, string | number | boolean]

此处一切正常,但有几点注意事项:

首先:为了提示编译器注意 shuffle([1, 2, 3]) 是对元组而不是数组进行操作,我给 array 函数参数 variadic tuple type [...T] 而不仅仅是 T。如果你删除它,你最终将不得不采取其他技巧来获得所需的行为(例如你的 checkThis() 函数在上下文中需要一个元组)。

其次:编译器​​不够聪明,无法执行必要的类型分析来验证 shuffle() 的实现实际上采用 [...T] 类型的 array 并生成Shuffled<T> 类型的结果。因此,我使用 type assertion 来告诉编译器 copy 在 returned 时可以被视为 as Shuffled<T>

Playground link to code