问题描述
问题的最小示例:
常量 a = {'X': {},'y': 'y',};常量 b = {'X': {},'y': 'y',};for (const p of ['x', 'y'] as const) {a[p] = b[p];//错误字符串 |{} 不可分配给 {} &细绳}for (const p of ['x', 'y'] as const) {开关 (p) {案例x":a[p] = b[p];//没有错误休息;案例'y':a[p] = b[p];//没有错误休息;}}Typescript 将 p
的类型视为 'x' |'y'
但与 a
和 b
由相同值索引的事实无关.
有没有办法……分发?联合操作使类型表达式从 a['x'|'y'] = b['x'|'y']
变为 a['x']= b['x'] |a['y'] = b['y']
我已经尝试了通用函数和一系列强制输入类型的尝试,但没有断言 as any
似乎没有任何效果.
我想避免类型 as
断言(尤其是对于 any
),因为它增加了一个类型分析可能会失败的区域,但我觉得我除了在构建期间生成代码之外,没有任何其他选项,但这是一个相当大的额外构建步骤,我宁愿不为这种感觉很小的东西添加.
switch 语句不是一个可行的替代方案,因为键的实际数量远大于 2,并且可能会发生变化,因此这将涉及在我的代码库中添加许多重复的代码部分.
这是 TypeScript 3.5 中引入的索引访问类型的类型安全改进,由 microsoft/TypeScript#30769.一般来说,允许 a[p] = b[q]
其中 a
和 b
是相同的对象类型并且其中 p
和 q
是相同的联合类型,因为如果 p
和 q
变成了不同的元素union,您可能正在编写一个不兼容的值.在您的情况下,您正在执行 a[p] = b[p]
,但从类型系统的角度来看,这是同一件事;它所看到的只是您正在编写和读取一个对象属性,其键是联合类型 "x" |y"
,这通常是不安全的.它没有注意到如果您在两个地方使用完全相同的值 p
,那么它必须是安全的.
因此,从 TypeScript 3.5 开始,这一直是一个痛点.当您读取或写入相同"属性时,需要解决此问题;请参阅 microsoft/TypeScript#32693.而且,幸运的是,根据此评论,看起来这将是修复了此处使用相同标识符(如 p
)作为键的情况.不过,不确定什么时候会发生……这个问题似乎在 Backlog 上,而不是针对特定版本的 TypeScript.所以可能需要一段时间.
在那之前,应该可以重构为泛型函数,因为在使用泛型类型时,它们仍然允许旧的 TS-3.5 之前的不安全访问.这在 评论 #30769 中提到:
我们一直有一个规则是任何给定的类型(以及,通过扩展,任何 T[K]
对于相同的 T
和 K
code>) 根据定义可分配给自身,这是我们允许的基本不健全
所以如果我们引入这个间接:
function copyProp(dst: T, src: T, key: K) {dst[key] = src[key];}
这编译得很好,现在我们可以使用它了:
for (const p of ['x', 'y'] as const) {copyProp(a, b, p);}
它也编译没有错误.这很烦人,但至少有一个目前有效的解决方案/解决方法,至少在 #32693 的修复发布之前是这样.
最后一个想法是希望可以在一般情况下修复此问题,这样您就可以避免使用 switch 语句.不久前,我打开了一个功能请求 microsoft/TypeScript#25051 以允许选择-in 分布式控制流分析",您可以在其中说类似 type switch (p) {...}
的内容,并让编译器为 联合类型的每个元素评估封闭的代码块一次>p
,如果每一次都成功,那么整个事情就会成功.编译器不可能对它遇到的每个联合类型的表达式进行这种多遍分析,但我希望我们至少可以在特定情况下有一些语法来要求它.唉,它不是(并且作为它要解决的几个问题之一的副本而关闭),但是当我看到这个问题时,我变得渴望并且 想想可能是什么.......叹息...
好的,希望有帮助;祝你好运!
Minimal example of the problem:
const a = {
'x': {},
'y': 'y',
};
const b = {
'x': {},
'y': 'y',
};
for (const p of ['x', 'y'] as const) {
a[p] = b[p]; // error string | {} is not assignable to {} & string
}
for (const p of ['x', 'y'] as const) {
switch (p) {
case 'x':
a[p] = b[p]; // no error
break;
case 'y':
a[p] = b[p]; // no error
break;
}
}
Typescript sees the type of p
as 'x' | 'y'
but doesn't correlate the fact that a
and b
are being indexed by the same value.
Is there a way to... distribute? the union operation so that essentially the typed expression becomes goes from a['x'|'y'] = b['x'|'y']
to a['x'] = b['x'] | a['y'] = b['y']
I've tried generic functions and a host of attempts at coercing the types of the inputs but short of asserting as any
nothing seems to work.
I'd like to avoid type as
assertions (especially to any
) as it adds one more area where type analysis is likely to fail, but I feel like I don't have any other options other than generating code during build but that's a rather large additional build step I'd rather not add for something that feels small like this.
A switch statement isn't a feasible alternative since the actual number of keys is much larger than 2 and is liable to change so this would involve adding many sections of duplicated code throughout my codebase.
This is a consequence of a type safety improvement to indexed access types introduced in TypeScript 3.5, as implemented by microsoft/TypeScript#30769. In general it is unsafe to allow a[p] = b[q]
where a
and b
are of the same object type and where p
and q
are of the same union type, since if p
and q
turn out to be different elements of the union, you might be writing an incompatible value. In your case you are doing a[p] = b[p]
, but from the type system's perspective that's the same thing; all it sees is that you are writing and reading an object property whose key is a union type "x" | "y"
, which is unsafe in general. It doesn't pay attention to the fact that if you're using the exact same value p
in both places, then it has to be safe.
So since TypeScript 3.5 this has been a pain point. There is a request to fix this for when you are reading or writing the "same" property; see microsoft/TypeScript#32693. And, fortunately, according to this comment it looks like this will be fixed for the case here where you're literally using the same identifier (like p
) as the key. Not sure when that will happen, though... the issue seems to be on the Backlog and not slated for a particular release of TypeScript. So it could be a while.
Until then it should be possible to refactor to a generic function, since one place they still allow the older pre-TS-3.5 unsafe access is when you are using generic type. This is mentioned in a comment on #30769:
So if we introduce this indirection:
function copyProp<T, K extends keyof T>(dst: T, src: T, key: K) {
dst[key] = src[key];
}
That compiles just fine, and now we can use it:
for (const p of ['x', 'y'] as const) {
copyProp(a, b, p);
}
which also compiles without error. It's annoying, but at least there is a solution/workaround that works for now, at least until a fix for #32693 is released.
One last thought about wishing this could be fixed in general so you could avoid switch statements. A while ago I opened a feature request microsoft/TypeScript#25051 to allow for "opt-in distributive control flow analysis" where you could say something like type switch (p) {...}
and have the compiler evaluate the enclosed code block once for each element of the union type of p
, and if each pass succeeded, then the whole thing would succeed. The compiler feasibly can't do that kind of multi-pass analysis for each union-typed expression it encounters, but I was hoping we could at least have some syntax to ask for it in specific cases. Alas, it is not to be (and was closed as a duplicate of one of the several issues it would address), but when I see this issue I become wistful and think of what might have been.... Sigh...
Okay, hope that helps; good luck!
这篇关于如何提示打字稿字符串在多个索引操作中是一致的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!