[英]Why TypeScript can't infer assignments of the same object type?
Let's say I have this very simplified example of what's happening in my code:假设我有一个非常简单的例子来说明我的代码中发生的事情:
interface I {
n?: number;
s?: string;
}
const a: I = {
n: 1,
}
const b: I = {
n: 2,
s: 'b',
}
const props = ['n', 's'] as const;
for (const prop of props) {
if (!a[prop]) {
// here, I get the following error:
// TS2322: Type 'string | number' is not assignable to type 'never'.
// Type 'string' is not assignable to type 'never'.
a[prop] = b[prop];
}
}
Being a
and b
the same types, accessing the same property prop
... shouldn't it be possible?作为
a
和b
相同的类型,访问相同的属性prop
......难道不应该吗?
b[prop]
is a number, then a[prop]
should accept numbersb[prop]
是一个数字,那么a[prop]
应该接受数字b[prop]
is a string, then a[prop]
should accept stringsb[prop]
是一个字符串,那么a[prop]
应该接受字符串b[prop]
is undefined, is because that prop is optional ?
b[prop]
未定义,是因为该 prop 是可选的?
So, what am I missing here?那么,我在这里错过了什么?
Update: Since I simplified my example too much, the answer was to remove the as const
part... But I think it's due to my environment, because I'm using strict: true
in tsconfig.json
...更新:由于我过多地简化了我的例子,答案是删除
as const
部分......但我认为这是由于我的环境,因为我在tsconfig.json
使用了strict: true
...
then, if I try accessing without as const
, I get this the TS7053 error:然后,如果我尝试不使用
as const
访问,则会收到 TS7053 错误:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'I'.
元素隐式具有 'any' 类型,因为类型 'string' 的表达式不能用于索引类型 'I'。
No index signature with a parameter of type 'string' was found on type 'I'.
在类型 'I' 上找不到带有类型 'string' 参数的索引签名。
Fix adding the as const
or doing for(const prop of props as ('n' | 's')[]) {
修复添加
as const
或执行for(const prop of props as ('n' | 's')[]) {
and then is when I get my original error (which can be easily fixed with as any
but I'm trying to avoid that)然后是当我得到我的原始错误时(可以很容易地修复
as any
错误,但我试图避免这种情况)
In general, Typescript doesn't prove every provable property of your code, and isn't trying to.通常,Typescript 不会证明代码的所有可证明属性,也不会尝试这样做。 In this particular case, we humans know that the type of
b[prop]
matches the type of a[prop]
because the property names are the same, but Typescript doesn't see that because it doesn't have a rule which would allow it to.在这种特殊情况下,我们人类知道的类型
b[prop]
类型匹配a[prop]
因为属性名相同,但打字稿没有看到,因为它没有一个规则,将允许它到。
If we follow Typescript's logic:如果我们遵循 Typescript 的逻辑:
props
is declared as a tuple of type ['n', 's']
. props
被声明为['n', 's']
类型的元组。prop
is declared as an element of props
, so its type is inferred as 'n' | 's'
prop
被声明为props
一个元素,所以它的类型被推断为'n' | 's'
'n' | 's'
. 'n' | 's'
。b
is declared as type I
, then b[prop]
is then inferred as type I['n' | 's']
b
被声明为类型I
,那么b[prop]
然后被推断为类型I['n' | 's']
I['n' | 's']
which simplifies to string | number
I['n' | 's']
简化为string | number
string | number
. string | number
。a
is declared as type I
, then the assignment target a[prop]
can only receive a value which is assignable to any property that prop
might name;a
被声明为类型I
,那么赋值目标a[prop]
只能接收一个可赋值给prop
可能命名的任何属性的值; so the assignment target's type is inferred as I['n'] & I['s']
which simplifies to string & number
, and then never
.I['n'] & I['s']
,它简化为string & number
,然后never
。a[prop] = b[prop]
is assigning a value of type string | number
a[prop] = b[prop]
是赋值string | number
类型的值。 string | number
to an assignment target of type never
. string | number
到类型为never
的赋值目标。 Since string | number
string | number
string | number
is not assignable to never
, this is a type error. string | number
不能分配给never
,这是一个类型错误。 The simplest fix is to use a type assertion like b[prop] as any
, ie you tell Typescript that you know your code is type-safe so it doesn't need to be checked.最简单的解决方法是使用像
b[prop] as any
这样的类型断言b[prop] as any
,即您告诉 Typescript 您知道您的代码是类型安全的,因此不需要检查它。 Another option is to use a helper function which does the assignments in a way that satisfies Typescript's type-checker:另一种选择是使用一个辅助函数,它以一种满足 Typescript 的类型检查器的方式进行赋值:
function copyMissingProperties<T>(a: T, b: T, props: readonly (keyof T)[]): void {
for (const prop of props) {
if (!a[prop]) {
a[prop] = b[prop]; // ok
}
}
}
Here, prop
's type is keyof T
which is not a union type, so the assignment target doesn't have an intersection type.这里,
prop
的类型是keyof T
,它不是联合类型,因此分配目标没有交集类型。
Here is one way I got it to work without making use of casting:这是我在不使用铸造的情况下使其工作的一种方法:
interface I {
n?: number;
s?: string;
}
const a: I = {
n: 1,
}
const b: I = {
n: 2,
s: 'b',
}
const props: Array<keyof I> = ['n', 's'];
for (const prop of props) {
if (!a[prop]) {
const newProp: I = {[prop]: b[prop]};
Object.assign(a, newProp);
}
}
The trick was to use Object.assign.诀窍是使用 Object.assign。 However, to still ensure the type safety, you can declare the new property as an object of type
I
and then use it in the Object.assign.但是,为了仍然保证类型安全,您可以将新属性声明为类型
I
的对象,然后在 Object.assign 中使用它。 To make this work in all cases, the best way to do is to just do Object.assign(a, {[prop]: b[prop]})
为了在所有情况下都能正常工作,最好的方法是执行
Object.assign(a, {[prop]: b[prop]})
the problem is with this line问题出在这条线上
const props = ['n', 's'] as const;
change it to将其更改为
const props = ['n', 's'];
check this stackblitz检查这个堆栈闪电战
I tried like that and its work, and you should remove as const value on props definition我试过这样和它的工作,你应该删除 props 定义上的 const 值
interface I {
n?: number;
s?: string;
}
const a: I = {
n: 1,
}
const b: I = {
n: 2,
s: 'b',
}
const props = ['n', 's'];
for (const prop of props) {
if (!a[prop]) {
a[prop] = b[prop];
console.log(a, b);
}
}
Result is: {n: 1, s: "b"} {n: 2, s: "b"}结果是:{n: 1, s: "b"} {n: 2, s: "b"}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.