简体   繁体   English

Typescript - 具有无效值的类型断言不会引发错误

[英]Typescript - type assertion with invalid value is not throwing error

I have a type assertion for the variable 'day', because of this when I assign boolean for the key, it doesn't throw any error.我对变量“day”有一个类型断言,因此当我为键分配布尔值时,它不会引发任何错误。

I am not doing like this const day: Day = 2 because the value I get is a string (which is a dynamic value) and I need to do a type assertion .我没有这样做const day: Day = 2因为我得到的值是一个字符串(这是一个动态值),我需要做一个类型断言

Could you please check this below example and suggest what needs to be changed to make it throw an error?您能否检查以下示例并建议需要更改哪些内容以使其引发错误?

    type Day = 0 | 1 | 2;

type TTime = {
    [K in Day]?: string
}

const day = 2 as Day;

const obj: TTime = {
    [day]: true // Expect this to throw error as 'boolean is not assignable to key
}

Another detailed example of what I am trying to resolve.我正在尝试解决的另一个详细示例。 I have to get the keys of another object(which is by default string[]) and do type assertion .我必须获取另一个对象的键(默认为 string[])并输入 assertion

type Day = 0 | 1 | 2;

type TTime = {
    [K in Day]?: string
}

const result: TTime = {
    2: 'I am result'
}

const keys = Object.keys(result);

let obj: TTime;
keys.forEach((key) => {
    const k = key as unknown as Day;
    const value = result[k]; //Key is string, so needs to be type asserted
    obj = {
        [k]: true //k is 2 in this case, but the value is boolean type and incorrect, this error
                  // is thrown when I remove the type assertion added and hardcode k as 2.
    }
})

You're unfortunately running into two TypeScript limitations or bugs... the first one is minor, but it leads to the major one.不幸的是,您遇到了两个 TypeScript 限制或错误……第一个是次要的,但它导致了主要的一个。


The minor issue is microsoft/TypeScript#13948 , in which an object with a computed property key of a union type is given an index signature which unnecessarily widens the object type to something less useful than you want.次要问题是microsoft/TypeScript#13948 ,其中具有联合类型计算属性键的对象被赋予索引签名,这不必要地将对象类型扩展为比您想要的不太有用的东西。 For example:例如:

const v = { [Math.random() < 0.5 ? "a" : "b"]: 123 };
// const v: { [x: string]: number;}

The value v will either be {a: 123} or {b: 123} at runtime.v在运行时将是{a: 123}{b: 123} It would therefore be nice if TypeScript inferred the type of v to be something like {a: number} | {b: number}因此,如果 TypeScript 推断v的类型类似于{a: number} | {b: number}就好了。 {a: number} | {b: number} . {a: number} | {b: number} Instead, it infers { [x: string]: number } ... so as far as the compiler is concerned, it has absolutely no idea what key names v has;相反,它推断{ [x: string]: number } ... 就编译器而言,它完全不知道v有什么键名; just that any properties it does have are of type number .只是它确实具有的任何属性都是number类型。

In your case, you have在你的情况下,你有

const obj = { [day]: true };
// const obj: { [x: number]: boolean; }

where obj has been widened from something like {0: boolean} | {1: boolean} | {2: boolean}其中obj已从{0: boolean} | {1: boolean} | {2: boolean} {0: boolean} | {1: boolean} | {2: boolean} {0: boolean} | {1: boolean} | {2: boolean} all the way to {[x: number]: boolean} , which has completely forgotten about Day . {0: boolean} | {1: boolean} | {2: boolean}一直到{[x: number]: boolean} ,它完全忘记了Day

This issue isn't catastrophic, since the actual type is at least compatible with the desired type, but it's suboptimal.这个问题不是灾难性的,因为实际类型至少与所需类型兼容,但它不是最理想的。


The second bug is microsoft/TypeScript#27144 , in which an object type with an index signature is assignable to a type with all optional properties , even if the property value types are incompatible .第二个错误是microsoft/TypeScript#27144 ,其中具有索引签名的对象类型可分配给具有所有可选属性的类型,即使属性值类型不兼容 This is pretty bad:这很糟糕:

const v: { [k: string]: number; } = {a: 1};
const x: { a?: string } = v; // no error!

The type of v has an index signature in which all properties must have a number -type value. v的类型具有索引签名,其中所有属性都必须具有number类型的值。 Meanwhile, the type of x has a single optional property of a string -type value if it is present.同时, x的类型具有string类型值的单个可选属性(如果存在)。 These should not be considered compatible, but they are.这些不应该被认为是兼容的,但它们是兼容的。 And so you can erroneously assign v to x without a compiler warning.因此,您可以在没有编译器警告的情况下错误地将v分配给x

In your case, you have:在您的情况下,您有:

const p: TTime = o; // no error

where the TTime type has all optional properties, and is considered compatible with the index-signature type of o , even though boolean and string are not compatible.其中TTime类型具有所有可选属性,并且被认为与o的索引签名类型兼容,即使booleanstring不兼容。

Yuck.呸。


So the interaction of those two issues is causing your problem.所以这两个问题的相互作用导致了你的问题。 The "right" thing to do here is probably to wait/lobby for microsoft/TypeScript#27144 to be fixed.在这里要做的“正确”事情可能是等待/游说 microsoft/TypeScript#27144 得到修复。 If you want to go to that issue and give it a 👍, it wouldn't hurt.如果您想解决那个问题并给它一个👍,那不会有什么坏处。 It probably wouldn't help much, either.它可能也不会有太大帮助。 Who knows when or even if it will be addressed.谁知道何时或什至会得到解决。

In the meantime, you have to deal with it.与此同时,你必须处理它。

One way to proceed is to make a workaround for microsoft/TypeScript#13948.一种方法是为 microsoft/TypeScript#13948 制定解决方法。 Instead of being satisfied with the index signature type, we can write a helper function which directly asserts that the value is of a more useful type.我们可以编写一个辅助函数来直接断言该值是更有用的类型,而不是满足于索引签名类型。 Like this:像这样:

const kv = <K extends PropertyKey, V>(
  k: K, v: V
) => ({ [k]: v }) as { [P in K]: { [Q in P]: V } }[K];

The kv function takes a key and a value and produces an object with one property corresponding to that key and value: kv函数接受一个键和一个值,并生成一个对象,该对象具有与该键和值对应的一个属性:

const y = kv("a", 123);
// const y: { a: number; }

If we do this when the key is of a union type, we get a union-typed output:如果我们在键是联合类型时这样做,我们会得到联合类型的输出:

const w = kv(Math.random() < 0.5 ? "a" : "b", 123);
// const w: {a: number} | {b: number}

And so you can use it to make obj :所以你可以用它来制作obj

const obj = kv(day, true);
// const obj: { 0: boolean; } | { 1: boolean; } | { 2: boolean; }

Now that obj does not have an index signature, we don't run into microsoft/TypeScript#27144:现在obj没有索引签名,我们不会遇到 microsoft/TypeScript#27144:

const obj: TTime = kv(day, true); // error,
// boolean is not assignable to string

The compiler is able to see that {0: boolean} | {1: boolean} | {2: boolean}编译器能够看到{0: boolean} | {1: boolean} | {2: boolean} {0: boolean} | {1: boolean} | {2: boolean} {0: boolean} | {1: boolean} | {2: boolean} is incompatible with TTime because boolean is incompatible with string , as desired. {0: boolean} | {1: boolean} | {2: boolean}TTime不兼容,因为booleanstring不兼容,如所愿。

Playground link to code Playground 代码链接

this way the error will show up;这样错误就会出现;


type Day = 0 | 1 | 2;

type TTime = {
    [K in Day]?: string
}

const day: Day = 2;

const obj: TTime = {
    [day]: true // Expect this to throw error as 'boolean is not assignable to key
}

The type assertion on day is causing the issue. day类型断言导致了这个问题。 You don't need an assertion (or even an annotation): primitives are immutable in JavaScript and TypeScript, so by declaring the variable using the const keyword, the number will always be the literal type 2 , and TypeScript knows this without additional syntax.您不需要断言(甚至不需要注释):原语在 JavaScript 和 TypeScript 中是不可变的,因此通过使用const关键字声明变量,数字将始终是文字类型2 ,TypeScript 无需额外语法就知道这一点。

TS Playground TS游乐场

type Day = 0 | 1 | 2;

type TTime = { [K in Day]?: string };

// This is the same type as above, using some type utilities:
// https://www.typescriptlang.org/docs/handbook/utility-types.html
// type TTime = Partial<Record<Day, string>>;

// Don't use a type assertion here. (https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions)
// If you prefer an annotation, you could write it this way:
// const day: Day = 2;
const day = 2;

const obj: TTime = {
  [day]: true, /*
  ~~~~~
  Type of computed property's value is 'boolean', which is not assignable to type 'string'.(2418) */
};

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

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