返回

如何巧用TypeScript类型体操实现Fill工具类型?

前端

TypeScript 类型体操:挑战 Fill 工具类型


在 JavaScript 中, Array.fill() 是一个很有用的方法,它可以将一个数组中的所有元素都填充为指定的值。在 TypeScript 中,我们可以利用类型体操来创建一个类似于 Array.fill() 的工具类型,叫做 Fill,它可以将一个泛型数组中的所有元素都填充为指定类型的元素,增强类型的可操作性。

迭代一:初探Fill工具类型

type Fill<T, N> = T extends unknown[] ? T : N[];

在这个迭代中,我们使用条件类型来检查 T 是否是一个数组。如果是,则直接返回 T。否则,则创建一个长度为 N 的数组,其中每个元素的类型都是 N

迭代二:引入泛型约束

type Fill<T, N> = T extends unknown[] ? T : N extends T ? T : N[];

为了确保 N 的类型与 T 的元素类型兼容,我们添加了一个泛型约束 N extends T。这个约束意味着 N 的类型必须是 T 的元素类型的子类型。

迭代三:解决递归问题

type Fill<T, N> = T extends unknown[] ? Fill<T[number], N> : N extends T ? T : N[];

T 是一个多维数组时,我们需要使用递归来填充数组。这个迭代中,我们使用 T[number] 来获取 T 的元素类型,然后递归地调用 Fill 来填充数组。

迭代四:优化类型推断

type Fill<T, N> = T extends unknown[] ? (T extends Array<infer E> ? Fill<E, N> : N[]) : N extends T ? T : N[];

为了提高类型推断的准确性,我们使用 infer 来推断 T 的元素类型 E。这样,当我们递归地调用 Fill 时,就可以使用 E 作为元素类型,从而避免类型推断错误。

迭代五:支持元组类型

type Fill<T, N> =
  T extends unknown[] ? (
    T extends [infer F, ...infer R] ? [Fill<F, N>, ...Fill<R, N>] : N[]
  ) : N extends T ? T : N[];

为了支持元组类型,我们需要对元组类型进行特殊的处理。这个迭代中,我们使用 [infer F, ...infer R] 来获取元组类型的第一个元素类型 F 和剩余的元素类型 R。然后,我们递归地调用 Fill 来填充元组类型的每个元素。

  • 将数组填充为默认值: 我们可以使用 Fill 工具类型来将一个数组填充为默认值。例如,我们可以使用 Fill<string[], '']> 来创建一个长度为 10 的字符串数组,其中每个元素都是空字符串。
  • 将对象数组填充为默认值: 我们可以使用 Fill 工具类型来将一个对象数组填充为默认值。例如,我们可以使用 Fill<{ name: string; age: number }, { name: ''; age: 0 }> 来创建一个长度为 10 的对象数组,其中每个对象的 name 属性和 age 属性都是空字符串和 0。
  • 将元组数组填充为默认值: 我们可以使用 Fill 工具类型来将一个元组数组填充为默认值。例如,我们可以使用 Fill<[string, number, boolean], ['', 0, false]> 来创建一个长度为 10 的元组数组,其中每个元组的第一个元素是空字符串,第二个元素是 0,第三个元素是 false

Fill 工具类型的工作原理是基于 TypeScript 的条件类型和泛型约束。它通过递归的方式将一个数组中的所有元素都填充为指定类型的元素。在填充过程中,Fill 工具类型会根据数组的类型和元素类型来决定如何填充数组。如果数组是多维数组,则 Fill 工具类型会递归地调用自身来填充数组。如果数组是元组数组,则 Fill 工具类型会特殊处理元组类型的第一个元素和剩余的元素,并递归地调用自身来填充数组。

通过五个版本的迭代,我们逐步实现了一个TypeScript版本的Fill工具类型。这个工具类型可以将一个泛型数组中的所有元素都填充为指定类型的元素,增强类型的可操作性。我们可以将 Fill 工具类型应用于各种场景,例如将数组填充为默认值、将对象数组填充为默认值、将元组数组填充为默认值等。