我试着写一段代码,为一个类型的每个成员生成一个有区别的联合体,以及一个函数,该函数将接受该类型的对象和一个联合体成员的示例。
这是我目前拥有的代码
interface RickAndMortyCharacter {
species: string;
origin: {
name: string;
url: string;
};
}
type RickAndMortyCharacterChange = {
[Prop in keyof RickAndMortyCharacter]: {
updatedProps: Prop;
newValue: RickAndMortyCharacter[Prop];
};
}[keyof RickAndMortyCharacter];
function applyPropertyChange(
state: RickAndMortyCharacter,
payload: RickAndMortyCharacterChange
) {
// Error on this line
state[payload.updatedProps] = payload.newValue;
return state;
}
也可在这里与 typescript 操场
typescript 给我以下错误
键入'string|{名称:字符串;url:字符串;}“不能分配给类型”字符串& { name:字符串; URL:string;}“。类型”string“不能分配给类型”string & { name:字符串; URL:string;}“。类型”string“不能分配给类型”{ name:字符串; URL:字符串;}'。(2322)
有趣的是,当我将鼠标悬停在RickAndMortyCharacterChange
类型上时,我看到声明了以下类型:
type RickAndMortyCharacterChange = {
updatedProps: "species";
newValue: string;
} | {
updatedProps: "origin";
newValue: {
name: string;
url: string;
};
}
这对我来说似乎是正确的,因为Typescript阻止我写这篇文章
const s: RickAndMortyCharacterChange = {
updatedProps: "origin",
newValue: "bonjour"
};
const s2: RickAndMortyCharacterChange = {
updatedProps: "species",
newValue: {
name: "Hello",
url: "Hoi"
}
}
我最近在一个Angular 项目中使用了一个非常非常相似的方法,它工作得很完美,我不明白为什么 typescript 在这里抱怨
2条答案
按热度按时间uplii1fm1#
这实际上是TypeScript类型检查算法的一个局限性,microsoft/TypeScript#30581中描述了根本问题。当您有一个联合类型的表达式,并且编写了该表达式多次出现的代码时,编译器通过将该表达式的每个语句视为独立于原始语句来分析它。
你会得到这个
注意,
up
和nv
都是联合类型,但是这丢失了你所关心的信息,如果你所知道的up
是"species" | "origin"
类型,而你所知道的nv
是string | { name: string; url: string}
类型,那么你就不能再写这样的代码而不出错:因为如果
up
是"origin"
,而nv
是string
呢?你只知道这是不可能的,因为up
和nv
都来自同一个payload
。但是TypeScript并不分析payload
的 identity,它只看 type。在TypeScript中没有“相关联合”这样的东西。所以赋值失败了,尽管它是100%类型安全的,而且他们不能通过让编译器多次分析
state[up] = nv
来修复它,而不严重破坏编译器的性能或使编译器不可预测。幸运的是,有一种方法可以重构代码,使编译器遵循逻辑。microsoft/TypeScript#47109对此进行了描述。该方法是用generics替换覆盖类似键类型的联合体,然后将通用的indexes执行为适当的mapped types。这些index-to-mapped类型可以发生在单个 * 分布式对象类型 * 中(如GitHub问题中所创造的)。
您的
RickAndMortyCharacterChange
本质上就是这样一种类型,但是您可以将其更改为泛型(并且仍然默认为完全联合):现在,
applyPropertyChange()
可以在K
中通用,并限制为keyof RickAndMortyCharacter
,你将传入一个RickAndMortyCharacterChange<K>
类型的值,突然间一切都正常了:如果将其分解为
up
和nv
,您将看到原因:现在
up
的类型是K
,nv
的类型是RickAndMortyCharacter[K]
,它们都是泛型,赋值可以看作是将右边的RickAndMortyCharacter[K]
赋值给左边的RickAndMortyCharacter[K]
,因为这些类型是相同的泛型,所以一切都按预期进行。Playground代码链接
t40tm48m2#
为了补充jcalz awesome answer的不足,可以创建一个实用程序类型,这样就可以轻松地将此模式应用于任何对象类型:
用途