简体   繁体   English

TypeScript - function 参数类型相等

[英]TypeScript - function argument types equality

I have a problem with using a generic in a class.我在 class 中使用泛型时遇到问题。 There's a class Item that accepts a generic T that extends constraining ItemValue type and there's a type Items that contains instances of Item typed as Item<ItemValue >.有一个 class Item接受一个扩展约束ItemValue类型的通用T ,并且有一个类型Items包含类型为Item<ItemValue > 的Item实例。

The problem is that the compiler does not pass a potentially valid generic argument through ItemValue constraint because callback property of Item is using it as a callback value.问题是编译器没有通过ItemValue约束传递潜在有效的泛型参数,因为Itemcallback属性将其用作回调值。

If I removed the callback property from the class, there would be no error.如果我从 class 中删除callback属性,则不会出现错误。

Further, I tried to replicate this without class, and there's no such error.此外,我尝试在没有 class 的情况下复制它,并且没有此类错误。 Do you think it's a bug, or am I doing something wrong?你认为这是一个错误,还是我做错了什么?

TypeScript Playground TypeScript 游乐场

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);
}

Update:更新:

As @Joey pointed out in his answer:正如@Joey 在他的回答中指出的那样:

string is assignable to string | number string可分配给string | number string | number , but (val: string) => void is not assignable to (val: string | number) => void string | number ,但是(val: string) => void不能分配给(val: string | number) => void

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

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

This narrows down the issue.这缩小了问题的范围。 My question now would be, why is that?我现在的问题是,为什么会这样?

Update 2更新 2

To be clear, I solved the issue in this example by defining private callback: Callback<ItemValue> = () => { } instead of private callback: Callback<T> = () => { } Having the generic in the callback parameter was not critical here.为了清楚起见,我通过定义private callback: Callback<ItemValue> = () => { }而不是private callback: Callback<T> = () => { }在回调参数中有泛型在这里并不重要。 The lesson here is to not define function types properties that have a generic, as then the class will be inextensible, eg Item<number> would not be assignable to Item<string | number>这里的教训是不要定义具有泛型的 function 类型属性,因为这样 class 将是不可扩展的,例如Item<number>不能分配给Item<string | number> Item<string | number> Also, in the real example, it was a dictionary of callbacks, in case you are wondering why I didn't define callback as a regular method. Item<string | number>此外,在实际示例中,它是一个回调字典,以防您想知道为什么我没有将callback定义为常规方法。

Note that string is assignable to string | number请注意, string可分配给string | number string | number , but (val: string) => void is not assignable to (val: string | number) => void . string | number ,但是(val: string) => void不能分配给(val: string | number) => void

In your example, Item<string> is not actually assignable to Item<string | number>在您的示例中, Item<string>实际上不能分配给Item<string | number> Item<string | number> because the callback properties don't have compatible types (ie (value: string) => void is not assignable to (value: string | number) => void ). Item<string | number>因为callback属性没有兼容的类型(即(value: string) => void不能分配给(value: string | number) => void )。

Edit:编辑:

To be clear, this is by design and is a natural consequence of the type union operator ( | ).需要明确的是,这是设计使然,并且是类型联合运算符 ( | ) 的自然结果。 Otherwise, the following code would compile, which would certainly be an issue.否则,下面的代码会编译,这肯定是个问题。

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

callback(4);

I changed this and it removed the error:我改变了这个,它消除了错误:

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

I believe it's because ItemValue can be a string OR a number so Item< string> wouldn't specific enough.我相信这是因为 ItemValue 可以是字符串或数字,所以 Item< string> 不够具体。 It would also work with:它也适用于:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM