【问题标题】:Why typescript complains that XXX is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint X?为什么打字稿抱怨XXX可以分配给类型'T'的约束,但'T'可以用约束X的不同子类型实例化?
【发布时间】:2022-01-20 06:58:14
【问题描述】:

我的代码是这样的:

export interface TreeItem {
  id: string;
  children: this[];
  collapsed?: boolean;
}

const createTreeItem = <T extends TreeItem>(): T => {
  return {
    id: 'root',
    children: []
  }
}

但我收到createTreeItem 的返回类型错误,如下所示:

TS2322: 类型 '{ id: string;孩子:从不[]; }' 不可分配给类型 'T'。 '{ 标识:字符串;孩子:从不[]; }' 可分配给“T”类型的约束,但“T”可以用约束“TreeItem”的不同子类型来实例化。

我完全不知道这是什么意思。

有什么帮助吗?

【问题讨论】:

    标签: javascript typescript interface typescript-generics


    【解决方案1】:

    假设你这样调用你的函数:

    let node = createTreeItem<TreeItem>()
    

    一切都好,对吧?返回类型TTreeItem,而您的泛型函数被硬编码返回的对象实际上确实具有将其限定为TreeItem 的类型:

    { id: string; children: never[]; }
    

    但是给你的函数一个类型参数的目的是允许它被其他类型的T调用,只要它们扩展TreeItem。所以以下应该是合法的调用:

    export interface BidiTreeItem {
      id: string;
      children: this[];
      parent: this;
      collapsed?: boolean;
    }
    
    let node = createTreeItem<BidiTreeItem>()
    

    调用是合法的,因为BidiTreeItem 满足约束T extends TreeItem。如您的函数定义中所声明的,此调用的返回类型是BidiTreeItem,但您的函数返回的不是BidiTreeItem

    如果您再次阅读错误消息,但记住上面的示例,现在对您来说就有意义了。但以防万一,下面我将翻译每条错误消息。请注意,第一句是结论,所以它将在此表中排在最后:

    this part of the error message... means this (using above example)...
    '{ id: string; children: never[]; }' is assignable to the constraint of type 'T' '{ id: string; children: never[]; }' is consistent with the constraint T extends TreeItem
    but 'T' could be instantiated with a different subtype of constraint 'TreeItem'. but T could be instantiated with a different subtype of TreeItem, for example BidiTreeItem.
    TS2322: Type '{ id: string; children: never[]; }' is not assignable to type 'T'. The object your function is returning is not guaranteed to be assignable to type T, because T could be some other subtype such as BidiTreeItem .

    【讨论】:

    • 很好的例子。 Here我写过一篇文章
    • @captain-yossarian,很好!顺便说一句,你觉得我让 Typescript 的人写一个措辞更好的错误信息怎么样?我在想一些类似的事情:“你已经陷入了两个经典错误之一!第一个从未设置noImplicitAny: false,但鲜为人知:永远不要在输入时违反参数化的返回类型约束安全危在旦夕!哈哈哈哈哈*程序死了*"
    • 我不太了解他们使用的错误消息策略。可能值得创建一些解释每个错误代码的文档。例如:TS2322 - means that .....
    • 你可以删除 :) 我会删除我的
    【解决方案2】:

    您可以在返回后添加“as T”:

    const createTreeItem = (): T => ({ id: '根', 孩子们: [] } 作为 T);

    【讨论】:

    • 这并不能回答问题,只会隐藏问题。如果你这样做了,那么一开始就没有理由使用泛型!
    • T 已经是扩展 TreeItems 的泛型类型,所以这不仅仅是隐藏问题。 Typescript 只需要知道您的对象返回的是什么类型,因为它不会被推断出来,因为您在使用 {} 创建对象时使用了 return,并且当您在代码上编写时,T 应该是函数结果的类型。
    • 看我的回答。您的“修复”破坏了将返回类型声明为T 的意义。如果实际返回类型忽略了类型参数T,那么它就是在撒谎。你的修复隐藏了这个谎言。如果这个想法是编译后的 Javascript 不需要知道谎言,那么为什么还要麻烦甚至有一个通用参数 T 呢?您只需声明此函数 (createTreeItem = (): { id: string; children: never[]; })
    猜你喜欢
    • 2019-12-13
    • 1970-01-01
    • 2021-12-10
    • 2023-01-11
    • 2020-11-25
    • 2021-06-19
    • 2021-02-13
    • 2021-10-14
    • 2021-08-14
    相关资源
    最近更新 更多