Why is it necessary to explicitly cast a non union typed variable to use it where a union type is expected?

huangapple go评论52阅读模式
英文:

Why is it necessary to explicitly cast a non union typed variable to use it where a union type is expected?

问题

以下是 TypeScript 代码的中文翻译部分:

interface SummaryTileDictionary { [ tileId: string ]: JQuery<HTMLElement>; }

class SummaryTileCount {
    tileId: string;
    tileCount: number;

    constructor(tileId: string, tileCount: number) {
        this.tileId = tileId;
        this.tileCount = tileCount;
    }

    updateCount(newCount: number): void {
        this.tileCount = newCount;
        tiles[this.tileId].val(newCount);
    };
}

let tiles: SummaryTileDictionary = {};
let tileCounts: SummaryTileCount[] = [];

tiles["tile-id"] = $("#tile-id");
const tileObject: JQuery<HTMLElement> = $("#" + "tile-id".replace(
    /^([a-z])|-([a-z])/g, _ => _.slice(_.length - 1).toUpperCase()));
myTileCounts.push(new SummaryTileCount(item.id, tileObject.val()));

请注意,我只翻译了代码部分,没有提供任何其他内容。如果您有任何其他问题或需要进一步的帮助,请随时告诉我。

英文:

If I have the following TypeScript:

interface SummaryTileDictionary { [ tileId: string ]: JQuery<HTMLElement>; }

class SummaryTileCount {
    tileId: string;
    tileCount: number;

    constructor(tileId: string, tileCount: number) {
        this.tileId = tileId;
        this.tileCount = tileCount;
    }

    updateCount(newCount: number): void {
        this.tileCount = newCount;
        tiles[this.tileId].val(newCount);
    };
}

let tiles: SummaryTileDictionary = {};
let tileCounts: SummaryTileCount[] = [];

tiles["tile-id"] = $("#tile-id");
const tileObject: JQuery<HTMLElement> = $("#" + "tile-id".replace(
    /^([a-z])|-([a-z])/g, _ => _.slice(_.length - 1).toUpperCase()));
myTileCounts.push(new SummaryTileCount(item.id, tileObject.val()));

I get the following error from tsc:

Argument of type 'string | number | string[]' is not assignable to parameter of type 'number'.
Type 'string' is not assignable to type 'number'.

Why does tsc infer that the value passed back from .val() is a string, if it's been defined as being of type string | number | string[]?

At the very least I feel as though the error message should be a little clearer about what's going on here...the way it's currently worded, it seems to a noob like me that string satisfies the condition string | number | string[]...

Anyway I know I can explicitly cast the .val() call's result to a number, but why should I have to? Why is TS not smart enough to do this for me?

NB: Although this question is tagged as relating to jQuery, it doesn't necessarily...I've seen this type of error far too many times for my liking and would love a general explanation as to why it happens.

答案1

得分: 1

Why does tsc infer that the value passed back from .val() is a string, if it's been defined as being of type string | number | string[]?

"为什么 tsc 推断从 .val() 返回的值是字符串,即使它已经被定义为 string | number | string[] 类型呢?"

It's not inferring that the value is actually a string. It's just stepping through the possible values and noticing that in the case where it's a string, problems ensue.

它并不是在推断这个值实际上是一个string。它只是遍历可能的值,并注意到在它是一个string的情况下会出现问题。

Typescript checks each member of the union to figure out if your code will work. If you're passing in a number, then that works fine, so no error for that case. But if you're passing in a string then that's not going to work, because SummaryTileCount requires a number.

TypeScript检查联合类型的每个成员,以确定你的代码是否有效。如果你传递一个number,那么一切都正常,因此在这种情况下不会出错。但如果你传递一个string,那就不行,因为SummaryTileCount要求一个number

So because of the mismatch for the string case, TypeScript shows the second line of the error, saying that string (the thing that might be passed in) is not assignable to number (the thing that SummaryTileCount asked for). This in turn means that the full type string | number | string[] is not assignable to number, which it shows as the first line of the error.

因此,由于string的情况不匹配,TypeScript显示错误的第二行,指出string(可能传递的值)不能赋值给numberSummaryTileCount要求的值)。这反过来意味着完整类型string | number | string[] 不能赋值给 number,这就是错误的第一行。

(string[] would cause a similar error, but for brevity TypeScript just shows the first error it finds.)

string[] 会导致类似的错误,但为了简洁起见,TypeScript只显示它找到的第一个错误。)

Anyway I know I can explicitly cast the .val() call's result to a number, but why should I have to? Why is TS not smart enough to do this for me?

"无论如何,我知道我可以将.val()的调用结果显式转换为数字,但为什么我必须这样做呢?为什么 TS 不足够智能,不自动为我做这个?"

Typescript is doing its job and pointing out that the types say this is not safe to do. If you want to use a type assertion, you can, but you should only do that if you have some extra knowledge that's not represented in the types.

TypeScript正在执行它的任务,并指出类型表示这样做是不安全的。如果你想使用类型断言,可以这样做,但只有在你有一些类型没有表示的额外知识的情况下才应该这样做。

new SummaryTileCount(item.id, tileObject.val() as number)

Keep in mind that a type assertion does not change what the value actually is at runtime, it just tells TypeScript at compile time "Trust me, I know what I'm doing, so don't check the types here." So if you do a type assertion when it's not correct, TypeScript won't be able to warn you about it.

请记住,类型断言不会在运行时改变值的实际内容,它只是告诉 TypeScript 在编译时"相信我,我知道我在做什么,所以不要在这里检查类型"。因此,如果你在不正确的情况下进行类型断言,TypeScript 将无法警告你。

Instead of a type assertion, the safe way would be to add code which checks to make sure it's actually a number. For example:

与其使用类型断言,更安全的方式是添加代码来确保它实际上是一个数字。例如:

const val = tileObject.val()
if (typeof val === 'number') {
  myTileCounts.push(new SummaryTileCount(item.id, val));
} else {
  // Insert logic to handle this case
}

<details>
<summary>英文:</summary>

&gt; Why does tsc infer that the value passed back from .val() is a string, if it&#39;s been defined as being of type string | number | string[]?

It&#39;s not inferring that the value is actually a `string`. It&#39;s just stepping through the possible values and noticing that in the case where it&#39;s a `string`, problems ensue. 

Typescript checks each member of the union to figure out if your code will work. If you&#39;re passing in a `number`, then that works fine, so no error for that case. But if you&#39;re passing in a `string` then that&#39;s not going to work, because `SummaryTileCount` requires a `number`. 

So because of the mismatch for the `string` case, typescript shows the second line of the error, saying that `string` (the thing that might be passed in) is not assignable to `number` (the thing that `SummaryTileCount` asked for). This in turn means that the full type `string | number | string[]` is not assignable to `number`, which it shows as the first line of the error.

(`string[]` would cause a similar error, but for brevity typescript just shows the first error it finds.)

&gt; Anyway I know I can explicitly cast the .val() call&#39;s result to a number, but why should I have to? Why is TS not smart enough to do this for me?

Typescript is doing its job and pointing out that the types say this is not safe to do. If you want to use a type assertion, you can, but you should only do that if you have some extra knowledge that&#39;s not represented in the types.

new SummaryTileCount(item.id, tileObject.val() as number)

Keep in mind that a type assertion does not change what the value actually is at runtime, it just tells typescript at compile time &quot;Trust me, i know what i&#39;m doing, so don&#39;t check the types here&quot;. So if you do a type assertion when it&#39;s not correct, typescript won&#39;t be able to warn you about it.

Instead of a type assertion, the safe way would be to add code which checks to make sure it&#39;s actually a number. For example:

const val = tileObject.val()
if (typeof val === 'number') {
myTileCounts.push(new SummaryTileCount(item.id, val));
} else {
// Insert logic to handle this case
}

huangapple
  • 本文由 发表于 2023年2月10日 10:39:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/75406433.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定