有时为了简化具有复杂输入的函数签名,我们定义一组“已知”复杂输入,每个输入与一个令牌相关联,并允许用户提供令牌。
我的函数适用于元组(这里简化为三元素元组),并且最初它没有任何编译器错误:
const TUPLE_LENGTH = 3
type Tuple = readonly [unknown, unknown, unknown]
function fn(tuple: Tuple): void {
if (tuple.length !== TUPLE_LENGTH) {
throw new Error(`Expected ${TUPLE_LENGTH} items, instead got ${tuple.length}`)
}
}
尝试一下。
…但是添加“已知”输入后,开始出现编译器错误:
const TUPLE_LENGTH = 3
type Tuple = readonly [unknown, unknown, unknown]
const knownTuples = {
foo: [1, 2, 3],
bar: ['a', 'b', 'c'],
baz: [true, false, null],
} satisfies Readonly<Record<string, Tuple>>
type TupleName = keyof typeof knownTuples
function fn(tuple: Tuple | TupleName): void {
if (typeof tuple === 'string') {
tuple = knownTuples[tuple]
}
if (tuple.length !== TUPLE_LENGTH) {
throw new Error(`Expected ${TUPLE_LENGTH} items, instead got ${tuple.length}`)
// ^^^^^^
// Error: Property 'length' does not exist on type 'never'
}
}
尝试一下。
出于某种原因,在第一种情况下,在
if (tuple.length !== TUPLE_LENGTH)
之后是tuple: Tuple
,但在第二种情况下,它被缩小到tuple: never
。这是为什么?
我怀疑,这与受歧视的工会有关,因为
Tuple
和string
都有财产length
,但我自己无法从中得出令人信服的答案。
代码已经是类型安全的,因为你不能发送长度不同于 3 的元组。因此第二个类型防护实际上使
tuple
类型为 never
,因此错误是合法的。
您不需要这种类型保护,它没有意义(并且 TS 显示该错误有效),只需将其删除即可。否则,您需要允许将数组而不是元组 [3] 传递给函数。