【问题标题】:TypeScript - function argument types equalityTypeScript - 函数参数类型相等
【发布时间】:2021-07-08 19:04:16
【问题描述】:

我在类中使用泛型时遇到问题。有一个类 Item 接受泛型 T 扩展约束 ItemValue 类型,还有一个类型 Items 包含 Item 的实例类型为 Item<ItemValue>。

问题在于编译器没有通过ItemValue 约束传递一个潜在有效的泛型参数,因为Itemcallback 属性将其用作回调值。

如果我从类中删除了callback 属性,则不会出现错误。

此外,我试图在没有类的情况下复制它,并且没有这样的错误。你认为这是一个错误,还是我做错了什么?

TypeScript Playground

type ItemValue = number | string;

type Callback<T> = (value: T) => void;

class Item<T extends ItemValue> {
  private value: T;

  // If you comment this out, there will be no error
  private callback: Callback<T> = () => { }

  constructor(value: T) {
    this.value = value;
  }
}

type Items = {
  [index: string]: Item<ItemValue>
};

let items: Items = {
    a: new Item<string>('') // error
}
// Type 'Item<string>' is not assignable to type 'Item<ItemValue>'.
//  Types of property 'callback' are incompatible.
//   Type 'Callback<string>' is not assignable to type 'Callback<ItemValue>'.
//      Type 'ItemValue' is not assignable to type 'string'.
//        Type 'number' is not assignable to type 'string'.


// This will also result in a similar error
// type AnItem = Item<ItemValue>;
// let a: AnItem = new Item<string>('');

// Here trying to do the same without a class.
function addCallback<T extends ItemValue>(cb: Callback<T>) {

}
function prepareAddCallback(cb: Callback<string>) {
    // No problems with putting Callback<string> to Callback<ItemValue>
    // Is it a bug?
    addCallback(cb);
}

更新:

正如@Joey 在他的回答中指出的那样:

string 可分配给string | number,但(val: string) =&gt; void 不可分配给(val: string | number) =&gt; void

type CallbackStrOrNum = (value: string | number) => void;
const addCallback = (cb: CallbackStrOrNum) => { };
addCallback((value: string) => { }); // error

let x: string | number = ''; // ok

这缩小了问题的范围。我现在的问题是,为什么会这样?

更新 2

为了清楚起见,我通过定义 private callback: Callback&lt;ItemValue&gt; = () =&gt; { } 而不是 private callback: Callback&lt;T&gt; = () =&gt; { } 解决了这个示例中的问题,在回调参数中使用泛型在这里并不重要。这里的教训是不要定义具有泛型的函数类型属性,因为这样该类将是不可扩展的,例如Item&lt;number&gt; 不能分配给 Item&lt;string | number&gt; 此外,在实际示例中,它是一个回调字典,以防您想知道为什么我没有将 callback 定义为常规方法。

【问题讨论】:

    标签: javascript typescript typescript-typings typescript-generics


    【解决方案1】:

    请注意,string 可分配给string | number,但(val: string) =&gt; void 不可分配给(val: string | number) =&gt; void

    在您的示例中,Item&lt;string&gt; 实际上不能分配给Item&lt;string | number&gt;,因为callback 属性没有兼容的类型(即(value: string) =&gt; void 不能分配给(value: string | number) =&gt; void)。

    编辑:

    需要明确的是,这是设计使然,并且是类型联合运算符 (|) 的自然结果。否则,下面的代码会编译,这肯定是个问题。

    const callback: (value: string | number) => void = (str: string) => {str.toLowerCase()};
    
    callback(4);
    

    【讨论】:

    • 确实,非常感谢!我更新了我的问题。它与回调的工作方式不同似乎有点奇怪。你知道这是为什么吗?
    • 编辑了我的答案,以提供一个例子来说明为什么这种行为是必要的。
    • 可能值得指出 the --strictFunctionTypes compiler flag 的文档,它解释了为什么需要检查函数参数 逆变 以确保类型安全(而不是通常的 协变 检查你需要的函数返回类型,或者不安全但有时有用的bivariant 检查编译器在引入之前对函数参数执行的操作)。跨度>
    【解决方案2】:

    我改变了这个,它消除了错误:

    let items: Items = {
        a: new Item<ItemValue>('') // (No) error
    }
    

    我相信这是因为 ItemValue 可以是字符串或数字,所以 Item 不够具体。它也适用于:

    let items: Items = {
        a: new Item<string | number>('') // (No) error
    }
    

    【讨论】:

    • 感谢您提出问题,但这根本不是问题。 string 应该作为 string | number 传递,并且确实如此 - 如果您删除 callbacks 属性或在我的最后一个示例中使用 addCallback。 (我没有对你投反对票)。
    猜你喜欢
    • 2013-11-03
    • 2021-07-15
    • 2018-08-28
    • 2020-04-27
    • 2012-09-23
    • 2016-11-01
    • 2020-11-10
    • 1970-01-01
    • 2021-02-07
    相关资源
    最近更新 更多