Typescript:深嵌套对象路径的设置值类型
我想找到一种方法,为嵌套对象路径的所有键提供所有Typescript:深嵌套对象路径的设置值类型,typescript,Typescript,我想找到一种方法,为嵌套对象路径的所有键提供所有值类型 我在一定程度上取得了成功,但未能为数组对象中的深层嵌套属性设置值类型 interface BoatDetails { boats: { boat1: string; boat2: number; }; ships: Array<Ship> } interface Ship { shipName: string } const boatDetails: BoatDetails = { boa
值类型
我在一定程度上取得了成功,但未能为数组对象中的深层嵌套属性设置值类型
interface BoatDetails {
boats: {
boat1: string;
boat2: number;
};
ships: Array<Ship>
}
interface Ship {
shipName: string
}
const boatDetails: BoatDetails = {
boats: {
boat1: "lady blue",
boat2: 1,
},
ships: [
{shipName: "lacrose"}
]
};
接口船详细信息{
船只:{
boat1:字符串;
船2:编号;
};
舰船:阵列
}
接口船{
船名:string
}
const boatDetails:boatDetails={
船只:{
船1:“蓝色女士”,
船2:1,
},
船舶:[
{船名:“拉克罗斯”}
]
};
对于上面的代码,我能够成功地为嵌套对象路径设置值类型,如boat1
的值类型为string
,boat2
的值类型为number
,的ships
的值类型为Array
但是无法为嵌套路径ships.0.shipName
设置值类型
我已从下面的链接中获取设置深嵌套对象路径类型的参考:
下面是我在typescript中设置深嵌套对象路径的值类型的尝试:
请注意:在路径
和路径值
中进行的操作对编译器来说是一种负担(您很容易得到显式递归限制警告,或者更糟糕的是,编译时间呈指数级增长),并且存在各种边缘情况
一种边缘情况是,使用模板文本轻松完成的从<代码>数字到<代码>字符串的转换不容易将<代码>字符串<代码>文本转换为等效的<代码>数字<代码>文本(有关更多信息,请参阅并回答),以及`形式的所谓模式模板文本(如中实现的)${number}.shipName`
当前不能直接用作对象类型的键(请参阅)
因此,我们为您提供了一个索引类型,如“0”
,您希望将其用作数组类型的键,但除非该数组类型恰好是,否则编译器将不允许您这样做:
type Oops = (string[])["0"] // error!
// ------------------> ~~~
// Property '0' does not exist on type 'string[]'
type Okay = (string[])[0] // okay
// type Okay = string
由于“0”
不被视为数组的键,“ships.0.shipName”
在路径值类型函数计算“0”扩展了Ship[]
时失败,您很难过。而且没有正式的方法将“0”
转换为0
,或者让编译器看到“0”
作为keyof Ship[]
,没有标准的解决方案
所以你有点被各种各样的解决方法所困扰。一种可能是忽略元组的可能性(除了那些讨厌的东西外,元组大多已经有显式的数字字符串索引),而只是做一个T[K]的解决方法
检查T
是否有number
索引签名,并且K
可分配给`${number}`
,如果是,则返回T[number]
:
type Idx<T, K> = K extends keyof T ? T[K] :
number extends keyof T ? K extends `${number}` ? T[number] : never : never;
类型Idx=K扩展了T?T[K]的范围:
数字扩展键T?K扩展`${number}`?T[number]:从不:从不;
现在起作用的是:
type TryThis = Idx<string[], "0">
// type TryThis = string
type StillWorks = Idx<string[], 0>
// type StillWorks = string
类型TryThis=Idx
//类型TryThis=string
StillWorks类型=Idx
//类型StillWorks=string
如果我们在您的PathValue
类型中使用它,如下所示:
type PathValue<T, P extends Paths<T, 4>> = P extends `${infer Key}.${infer Rest}`
? Rest extends Paths<Idx<T, Key>, 4>
? PathValue<Idx<T, Key>, Rest>
: never
: Idx<T, P>
type PathValue=P扩展了`${infer Key}.${infer Rest}`
?Rest扩展路径
?路径值
:从不
:Idx
然后事情开始运作:
setValue(
boatDetails,
`ships.0.shipName`,
"titanic"
); // okay
/* function setValue<BoatDetails, "ships.0.shipName">(
obj: BoatDetails, path: "ships.0.shipName", value: string
): BoatDetails */
setValue(
船的细节,
`ships.0.shipName`,
“泰坦尼克号”
)好的
/*函数设置值(
对象:BoatDetails,路径:“ships.0.shipName”,值:string
):船只详情*/
还有其他可能的解决方法,可以从更任意的T
和K
对中挑出更精确的结果,但我认为这就足够了
这是否回答了您的问题?抱歉,它没有回答,但感谢您对此进行研究。我相信问题在于0
索引没有强制显示在数组中类型。如果您将ships
的类型切换为[Ship]
您的代码可以工作。请注意setValue()中的V
type参数
函数实际上并没有什么作用;您可能应该删除它,并直接用PathValue
替换对V
的所有引用。感谢您给出如此令人惊讶的答案。您对typescript编译器工作原理的了解对typescript社区有很大帮助。