【问题标题】:What is the reason for this `Type 'any[]' is not assignable to type '[number]` TypeScript error?这个 `Type 'any[]' is not assignable to type '[number]` TypeScript 错误的原因是什么?
【发布时间】:2022-01-26 14:35:17
【问题描述】:
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)

tested against the TypeScript Compiler 这个代码因为Type 'any[]' is not assignable to type '[number]' 而抛出错误。

我的直觉是定义的fn 被分配给通用的Data 值,但错误表明分配方向相反。这是 TypeScript 编译器的错误还是限制?或者我应该如何思考才能有更好的直觉?

【问题讨论】:

  • 我知道@AluanHaddad 的区别。关键是,就我而言,fn 适合Fn 接口,因为[number] 可以分配给any[]。错误是不能将any[] 分配给[number] ...虽然这显然是正确的,但为什么要朝那个方向分配?
  • 对不起,我误读了这个问题。我的错。
  • @alecmce 你不是想在这里做相反的事情吗?您不是将数字传递给 any,而是尝试将 any 传递给数字。
  • @Andrew,不。我认为你在这里搞错了。请查看问题中的链接。 Depsany[] 类型的数组。 fn 表达式使用一个元组[number],它不是number[],而是一个正好为1 值的数字数组。因此,[number]any[] 的子类型。我正在尝试将更具体的类型传递给更一般的情况。
  • @alecmce 不,你倒退了,这就是你问这个问题的原因。您试图断言一个类型比它定义的更具体。您正在尝试将定义为类型为 any[] 的值传递给一个断言它是 [number] 的函数,您在函数签名中断言的内容比 yiu 在您打算传递的类型中所做的更具体,因此 any[] 正在发生into [number] 无效,不会编译。

标签: typescript typescript-generics


【解决方案1】:

同样问题的非常简化的例子:

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 只知道如何粉碎苹果。所以你不能appleSmasher当作FruitSmasher对待。

Playground


这与您的直觉相反,因为此处的Fn 需要可以使用any[] 作为Deps 进行调用。这意味着[123]['asd', 123][] 都需要被允许作为Deps 才能被视为与Fn 相同的类型。

但是,fn 实际上要求 Deps[number],这意味着 any[] 不会起作用。

所以根据Fn的类型。这应该是有效的:

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: [] })

但是fnDeps 现在不满意。 data.list[0] 会返回 undefined,然后会在 .toString() 上崩溃。

所以是的,[number] 可以分配给any[]。但是,当函数的参数被声明为 any[] 时,任何更多比它更具体的东西都会破坏它,因为不能保证该函数是它需要的类型。

【讨论】:

  • 我明白了,所以它是逆变的。驼峰。好的。我想让我感到困惑的是错误:``` Type '(data: Data) => void' is notassignable to type 'Fn'。参数 'data' 和 'data' 的类型不兼容。类型 'Data' 不可分配给类型 'Data'。类型“any[]”不可分配给类型“[number]”。目标需要 1 个元素,但源可能有更少。 ``` 切换了最后一行的赋值方向,看起来像一个错误。
【解决方案2】:

这就是正在发生的事情。你为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 接口的对象传递给无法接受的对象该接口的一些可能形式。

请记住,打字稿实际上只在 IDE 和编译器中起作用。它最终被内置到 JavaScript 中,这是一种没有数据类型的解释性语言。这意味着函数 fn 如果在编译后尝试传递一个包含数组中字符串的对象,它实际上不会抛出错误,因此 IDE 和编译器需要标记它并拒绝接受结构问题。

【讨论】:

    猜你喜欢
    • 2022-09-22
    • 1970-01-01
    • 2022-12-27
    • 2022-12-19
    • 2022-12-02
    • 2022-11-20
    • 1970-01-01
    • 2020-08-27
    • 2022-12-27
    相关资源
    最近更新 更多