TS的数组类型你使用哪个?Array<string>
(泛型语法)与 string[]
(数组语法),这两种表示法在功能上完全没有区别。但其中总是涉及细微差别和权衡。通常情况下,泛型表示法要好得多。
关于这个问题时,一起分析下两种写法的不足和优势
数组语法更短
就是这样。这就是优势所在。写入的字符更少。好像代码越短就越易维护一样。
但我们更经常阅读代码而不是编写它,因此我们不应该专注于使其易于编写——它应该易于阅读。
泛型语法可读性
我们通常从左到右阅读,所以更重要的东西应该先出现。我们说:“这是一个字符串数组”,或者“这是一个字符串或数字数组”。
1 | //从左到右 |
如果我们数组中的类型是相当长的,则这一点尤为重要,例如因为它是从某处推断出来的。IDE 通常以数组表示法显示数组类型,所以有时,当我悬停在一个对象数组上时,我会得到这样的内容:
1 | //options-array |
这读起来像是 options 是一个对象,只有在最后,我才能看到它实际上是一个数组。如果对象有很多属性,情况就会变得更糟,因为这会使内容变得更长,并且会给浮动框增加一个滚动条——使得很难看到最后的 []。诚然,这可能是一个工具问题,但如果我们将其显示为它的本质:一个对象数组,这个问题就不存在了。当跨多行展示时,它甚至不会长得多:
1 | //array-of-options |
不管怎样,我们继续往下看,因为这不是数组表示法的唯一优点。
ReadonlyArray
让我们面对现实——我们作为函数输入的大多数数组都应该是只读的,以避免意外的突变。我也在另一篇文章中提到了这个话题。如果你使用泛型表示法,你可以简单地将Array
替换为ReadonlyArray
并继续前进。如果你使用数组表示法,你必须将它拆分成两部分:
1 | ## readonly-arrays |
//array-of-unions
// ✅ 和以前完全一样
function add(items: Array<string | number>, newItem: string | number)
1 | 然而,如果使用数组表示法,情况开始变得奇怪 |
//string-or-number-array
// ❌ 看起来还行,但实际不是
function add(items: string | number[], newItem: string | number)
1 | 如果你无法立即发现错误——那就是问题的一部分。它隐藏得如此之深,以至于需要一些时间才能看到。让我们通过实际实现该函数并查看我们收到的错误来帮助一下: |
//not-assignable
// ❌ 为什么这不起作用 😭
function add(items: string | number[], newItem: string | number) {
return items.concat(newItem)
}
1 | 显示以下错误: |
Type ‘string’ is not assignable to type ‘ConcatArray
TypeScript playground
1 | 对我来说,这一点毫无意义。为了解决这个谜题:这涉及到运算符优先级。[] 的绑定强度比 | 运算符更强,所以现在我们已经使 items 成为了 string OR number[] 类型。 |
//pick
const myObject = {
foo: true,
bar: 1,
baz: ‘hello world’,
}
pick(myObject, [‘foo’, ‘bar’])
1 | 我们只想允许现有键作为第二个参数传递,那么我们该怎么做呢?使用 `keyof `类型操作符: |
//pick-generic-notation
function pick<TObject extends Record<string, unknown>>(
object: TObject,
keys: Array
)
1 | 当我们使用泛型语法为数组时,一切都正常。但是如果我们将其更改为数组语法会发生什么? |
//pick-array-notation
function pick<TObject extends Record<string, unknown>>(
object: TObject,
keys: keyof TObject[]
)
1 | 令人惊讶的是,没有错误,所以这应该是正确的。没有错误甚至更糟,因为这里存在错误——它只是不会显示在函数声明中——而是在我们尝试调用它时出现: |
pick(myObject, [‘foo’, ‘bar’])
1 | 现在产生: |
Argument of type ‘string[]’ is not assignable to parameter of type ‘keyof TObject[]’.(2345)
1 | 这条消息比之前的消息更加让人摸不着头脑。为什么我的键不是字符串?不知道 `keyof TObject[] `的合法输入是什么。只知道定义我们想要的正确方式是:`(keyof TObject)[]` |
//fixed-array-notation
function pick<TObject extends Record<string, unknown>>(
object: TObject,
keys: (keyof TObject)[]
)
```
这些都是我在使用数组表示法时遇到的问题。