interface Fn {
(data: Data): void
}
interface Data<Value = any, Deps extends any[] = [...any]> {
value: Value
list: Deps
}
function fn(data: Data<string, [number]>): void {
console.log(data)
}
const foo: Fn[] = [fn]
console.log(foo)
当针对TypeScript编译器进行测试时,此代码抛出一个错误,因为Type 'any[]' is not assignable to type '[number]'
。
我的直觉是,定义的fn
被分配给通用的Data
值,但错误表明分配朝着另一个方向进行。这是TypeScript编译器的错误还是限制?或者我应该如何思考才能有更好的直觉?
相同问题的大量简化示例:
type FruitSmasher = (fruit: 'apple' | 'orange' | 'pear') => void
const appleSmasher: FruitSmasher = (apple: 'apple') => undefined
// Type '"apple" | "orange" | "pear"' is not assignable to type '"apple"'.
appleSmasher('orange') // this is bad.
appleSmasher
不能归属于FruitSmasher
类型,因为水果粉碎器可以粉碎任何水果,但appleSmasher
只知道如何粉碎苹果。因此,您不能像对待FruitSmasher
那样对待appleSmasher
。
游乐场
这是你的直觉,因为这里的Fn
需要用any[]
作为Deps
来调用。这意味着[123]
、或['asd', 123]
或[]
都需要被允许作为Deps
以便被认为是与Fn
相同的类型。
然而,fn
实际上要求Deps
是[number]
,这意味着any[]
不起作用。
因此根据CCD_ 21。这应该是有效的:
function fn(data: Data<string, [number]>): void {
console.log(data.list[0].toString()) // this will crash
}
const foo: Fn[] = [fn] // ignore this very valid type error for now
// Valid for type `Fn`, catastrophic for function `fn`.
foo[0]({ value: 123, list: [] })
然而,fn
的Deps
现在并不满意。data.list[0]
将返回undefined
,然后在.toString()
上崩溃。
所以是的,CCD_ 27可分配给CCD_ 28。但是,当函数的参数被声明为any[]
时,任何比这更具体的都会破坏这一点,因为该函数无法保证其所需的类型。
下面是发生的事情。您为Data
类型声明接口,表示该类型的第二个值是一个可以具有任何类型的数组:
interface Data<Value = any, Deps extends any[] = [...any]> {
value: Value
list: Deps
}
然后你使用数据类型,但说第二个值将是一个数字数组:
function fn(data: Data<string, [number]>): void {
console.log(data)
}
因此,类型表示数组可以是任何东西,但函数声明表示Data
类型保证数组只能是数字,因为您正在将符合Data
接口的对象传递到无法接受该接口的某些可能形式的对象中。
请记住,typescript实际上只在IDE和编译器中起作用。它最终被构建到JavaScript中,JavaScript是一种没有数据类型的解释语言。这意味着,如果您试图在编译函数fn
后将数组中包含字符串的对象传递给它,那么它实际上不会抛出错误,因此IDE和编译器需要标记它并拒绝接受结构问题。