有没有办法让这样的工作正常工作?
annotation(Dialog(group = "GroupName"))
答案 0 :(得分:2)
这是由于最近在TypeScript 3.5中引入的breaking change(有关更多信息,请参见relevant pull request)提高了写入indexed-access/lookup types时的可靠性。简而言之,您所做的只是安全的,因为您碰巧知道您正在将值写回到刚刚读取的属性中。但是编译器没有意识到这一点,因为您有意将属性键的类型扩展为并集。因此,x2
的类型为A | B | undefined
,将这种类型写回到x[name2]
是不安全的:
const name2 = "AA" as "AA" | "BB";
let x2 = x[name2]; // type: A | B | undefined
x2 = { b: "okay" }; // works because x2 is of A | B | undefined
x[name2] = x2; // now this error makes sense, right?
如我所见,这里的问题是我一直称呼"correlated types"的语言缺乏支持。您知道类型x2
的两个联合类型的表达式x[name2]
和A | B | undefined
并不相互独立。它们要么都是A | undefined
,要么都是B | undefined
。但是编译器通常无法表达这种依赖性。这里的解决方法通常涉及泛型,类型断言或代码重复。
泛型:使编译器理解其安全性的一种方法是,将您正在做的事情抽象为泛型函数,其中名称是扩展了"AA" | "BB"
的泛型类型:
function genericVersion<N extends "AA" | "BB">(name: N) {
const x3 = x[name];
x[name] = x3; // no error
}
genericVersion("AA" as "AA" | "BB");
之所以行之有效,是因为当T[N]
是泛型参数时,编译器允许您将类型T[N]
的值分配给类型N
的变量。仍然是technically unsafe:
function unsafeGeneric<N extends "AA" | "BB">(readName: N, writeName: N) {
const x4 = x[readName];
x[writeName] = x4; // no error
}
unsafeGeneric("AA", "BB"); // no error... oops
但是允许它比禁止它更有用。
断言:另一个解决方法是通过type assertion放宽用于分配的x
的类型,如:
type Loosen<T extends object, K extends keyof T> = {
[P in keyof T]: P extends K ? T[K] : T[P]
};
(x as Loosen<typeof x, typeof name2>)[name2] = x2; // okay
类型Loosen<T, K>
接受一个对象类型T
及其一组键K
,并返回一个更宽泛的类型,其中T
中的任何属性类型都带有键K
可以分配给任何人。例如,
type L = Loosen<{ a: string; b: number; c: boolean }, "b" | "c">;
等同于
type L = {
a: string;
b: number | boolean;
c: number | boolean;
}
因此,如果我将类型boolean
的值扩展为{{,则可以写b
到number
和c
到{ a: string; b: number; c: boolean }
,而不会抱怨1}}。当您进行这样的断言时,您需要注意不要对编译器撒谎,并在错误的属性中放置错误的值。
复制:最后一种解决方法是手动遍历所有可能性的编译器……这是完全类型安全的,但具有冗余性:
L
恐怕我没有一个很好的答案。 TS3.5中改进的健全性是100%正确的,并且确实捕获了错误,但也为开发人员带来了很多麻烦。通常,我倾向于使用类型断言,因为它通常是实现目标的破坏性最小的更改。
哦,很好。希望有帮助!祝你好运。