Javascript 如何在TypeScript中实现静态类型的不可变redux状态树?
使用TypeScript+Redux并能够定义静态类型的不可变状态树,如下所示:Javascript 如何在TypeScript中实现静态类型的不可变redux状态树?,javascript,typescript,redux,Javascript,Typescript,Redux,使用TypeScript+Redux并能够定义静态类型的不可变状态树,如下所示: interface StateTree { readonly subState1: SubState1; readonly subState2: SubState2; } interface SubState1 { readonly a: number; readonly b: string; } interface SubState2 { readonly c: { readonl
interface StateTree {
readonly subState1: SubState1;
readonly subState2: SubState2;
}
interface SubState1 {
readonly a: number;
readonly b: string;
}
interface SubState2 {
readonly c: {
readonly d: symbol;
readonly e: string;
};
}
…非常棒,因为它在很大程度上避免了对库的需要,并允许访问只读状态树,如:
const state: StateTree = getState();
state.subState2.c.e
然而,在创建只读状态树的修改副本时,有没有办法保持不变性和类型安全性
我认为Object.assign将由任务决定,并且从不变性的角度来看,但由于TypeScript的类型交叉行为,它在编译时类型安全性上失去了优势。readonly是不变性工具箱中的一个好工具,但它仅在编译时强制执行
immutable.js进一步保证了运行时的不变性
据我所知,没有一种魔杖能同时提供深度克隆和类型安全。Object.assign仅提供浅复制,但这通常足以用于Redux中的状态
我不同意Object.assign会降低编译时安全性。Typescript的结构类型系统将通过确保所有强制成员都已实际分配来检查兼容性,因此
interface State {
a: number;
}
const myVar = Object.assign({}, {b: 456}) as State;
无法编译,因为myVar中缺少a通过讨论阅读后,我得出结论,我对Object.assign的原始想法因TypeScript的类型交叉而不正确,原因如下:
TypeScript的类型交集不是我最初问题的根源,而是对象分配。
我一直在关注交叉路口类型,而这是根本原因:
interface Combined {
a: number;
b: string;
}
interface A {
a: number;
}
interface B {
b: string;
}
const temp1: Combined = {
a: 1,
b: '2'
};
const temp2: A = temp1;
…不涉及类型交叉点
接口是接口,而不是具体类型。
关于上面的代码,我一直在思考为什么最后一行是const temp:A=temp1;是有效的,通过类型交叉的面纱
揭开面纱,我意识到这是有充分理由的
A不像原语或类那样是一个具体类型,它是一个接口
结论
让我们回到原来的问题上来
TypeScript的接口和readonly以及JavaScript的Object.assign是编译时类型安全性和不变性的最佳选择
剩下的检查责任必须落在诸如单元测试、代码审查等实践上。我犯了React&Redux新手错误,在我第一次设置项目时,没有对不变性给予足够的重视,现在我开始考虑对其进行改造。啊。我一直在评估不同的方法,非常强调保留TypeScript的强类型。我没有深入到Immutable.js,因为我没有看到用它保存类型检查的方法,至少在我的项目中没有。然后我偶然发现了一个不可更改的任务
ImmutableAssign的一个既定设计目标是很好地使用TypeScript,到目前为止,这对我来说似乎效果不错。它的行为似乎与我所希望的完全一样,在一个嵌套很深的对象链上更改属性会更改路径中所有对象的引用。不幸的是,在我的项目改造过程中,我还没有对ImmutableAssign的性能特征做出真正的评论。尽管如此,您可能还是想看一看。使用TypeScript的主要目的是获得编译时保证,因此我不是在寻找运行时不变性保证,而是在寻找编译时保证。正如我在回答另一个/链接的问题时提到的,您会发现您的代码确实编译没有错误。不,这段代码没有编译:检查My error,它有,但尝试执行类似于此对象的操作。分配{},状态,{b:456};,其中state的类型是state,它会编译,出于某种原因。这就是结构类型系统在Typescript和大多数其他语言中的工作方式顺便说一句:新的var与state“兼容”,即使你用b对其进行了扩充,因为它拥有state的所有成员。对了,谢谢,这对我来说现在是有意义的,但不幸的是,我的问题仍然没有得到回答。这肯定是一个替代方案,但我个人并不喜欢这种机制,尽管API的侵入性较小。TypeScript在spread操作符方面变得更加智能,我发现它在类型安全方面非常有效,即使样板文件的构建与嵌套成比例,但这正是使用不支持不变性的语言所得到的。