TypeScript中的联合类型分配

时间:2019-07-03 16:58:49

标签: typescript

有没有办法让这样的工作正常工作?

annotation(Dialog(group = "GroupName"))

1 个答案:

答案 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的值扩展为{{,则可以写bnumberc{ a: string; b: number; c: boolean },而不会抱怨1}}。当您进行这样的断言时,您需要注意不要对编译器撒谎,并在错误的属性中放置错误的值。


复制:最后一种解决方法是手动遍历所有可能性的编译器……这是完全类型安全的,但具有冗余性:

L

恐怕我没有一个很好的答案。 TS3.5中改进的健全性是100%正确的,并且确实捕获了错误,但也为开发人员带来了很多麻烦。通常,我倾向于使用类型断言,因为它通常是实现目标的破坏性最小的更改。

哦,很好。希望有帮助!祝你好运。

Link to code