Javascript typescript动态安全地向数组添加元素
我正在将Javascript应用程序移植到Typescript。 我有一个对象,它有许多包含数据的数组属性,还有一个javscript函数可以访问数据数组。为了简单起见,我只考虑Add方法< /P>Javascript typescript动态安全地向数组添加元素,javascript,typescript,dynamic,types,Javascript,Typescript,Dynamic,Types,我正在将Javascript应用程序移植到Typescript。 我有一个对象,它有许多包含数据的数组属性,还有一个javscript函数可以访问数据数组。为了简单起见,我只考虑Add方法< /P> function updateDataStructure(state, structureName, elem) { state.data[structureName].push(elem); return state.data[structureName]; } 移植到typescr
function updateDataStructure(state, structureName, elem) {
state.data[structureName].push(elem);
return state.data[structureName];
}
移植到typescript我想介绍类型安全性并维护通用代码
interface AA {
lep: string,
snap: number,
sul: boolean
}
interface BB {
p1: string;
val: number;
}
interface StateDataContainer {
aa: AA[],
bb: BB[],
}
class State {
data: StateDataContainer;
}
export type DataContainerProps = keyof StateDataContainer;
type ArrayElement<ArrayType> = ArrayType extends (infer ElementType)[] ? ElementType : never;
function updateDataStructure<K extends DataContainerProps, D extends ArrayElement<StateDataContainer[K]>>
(state: State, structureName: K, elem: D)
: StateDataContainer[K] {
// @ts-ignore
state.data[structureName].push(elem);
return state.data[structureName];
}
使用此解决方案,我必须在实现代码中添加使用//@ts ignore注释,以避免编译器错误,如:
Argument of type 'D' is not assignable to parameter of type 'AA & BB'.
Type 'ArrayElement<StateDataContainer[K]>' is not assignable to type 'AA & BB'.
Type '{}' is not assignable to type 'AA & BB'.
Type '{}' is not assignable to type 'AA'.
Type 'AA | BB' is not assignable to type 'AA & BB'.
类型为“D”的参数不能分配给类型为“AA&BB”的参数。
类型“ArrayElement”不可分配给类型“AA&BB”。
类型“{}”不可分配给类型“AA&BB”。
类型“{}”不可分配给类型“AA”。
类型“AA | BB”不可分配给类型“AA&BB”。
有没有更好的方法来编写此代码 您的
结构名称
参数的类型为'aa'|'bb'
,您的元素
类型为aa | bb
。TypeScript目前没有像您预期的那样缩小泛型(请参阅)
在您的示例中,请注意输入/输出类型仍然正确(如果您的structureName
为'aa'
,则无法将行
传入)。因此,如果您愿意在函数实现中牺牲一些类型安全性,下面是一个让您解锁的黑客:
state.data[structureName as 'aa'].push(elem as AA);
我认为另一个问题是TypeScript不理解
ArrayElement
类型的语义,也就是说,它不知道它意味着可以附加到StateDataContainer[K]
的内容
解决这个问题的一种方法是用D
来定义state
,即
function updateDataStructure<K extends DataContainerProps, D extends ArrayElement<StateDataContainer[K]>>
(state: { data: { [key in K]: D[] } }, structureName: K, elem: D)
: D[] {
state.data[structureName].push(elem);
return state.data[structureName];
}
函数更新的数据结构
(状态:{data:{[key in K]:D[]},结构名称:K,元素:D)
:D[]{
state.data[structureName].push(元素);
返回状态。数据[structureName];
}
类型脚本:是的,你是对的,在方法体中元素的类型是AA | BB。我认为这不是一个安全问题,因为在push时,编译器向我们保证structureName的值和elem的类型将一致。打开structureName值并为每个值添加显式强制转换的自定义push将增加降低安全性的代码行数。我在想是否有可能将
AA | BB转换为AA&BB
老实说,我甚至在考虑将它进一步向相反的方向移动,并将structureName
和elem
设置为any
。在这一点上,更多的变化最多只能给你带来边际收益。我建议在这段代码上面抛出一个//TODO
,然后等待编译器在将来的版本中正确处理它。是的,好主意,如果我不想修改签名,我也可以将数组(state.data[structureName]转换为d[])。push(elem)
和state.data[structureName]=(state.data[structureName]作为D[])。filter(v=>v)作为StateDataContainer[K]代码>
function updateDataStructure<K extends DataContainerProps, D extends ArrayElement<StateDataContainer[K]>>
(state: { data: { [key in K]: D[] } }, structureName: K, elem: D)
: D[] {
state.data[structureName].push(elem);
return state.data[structureName];
}