javascript - 如何在 TypeScript 中创建 RequireOnlyOne 嵌套类型?
问题描述
我经常看到这个代码片段:
type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys]
在这里你可以找到我从哪里提出的问题。
它有效,但是,我有这个结构:
export interface MenuItems {
firstLevel: {
secondLevel: ['one', 'two']
};
anotherFirstLevel: {
anotherSecondLevel: ['one', 'two'],
oneMoreSecondLevel: null
};
}
我需要申请RequireOnlyOne
第一级和第二级,但我不知道要在类型上更改什么,RequireOnlyOne
因此它适用于每个 firstLevel 键,但也适用于 secondLevel 键。就像现在一样,我只能选择一个 firstLevel,但可以选择该 firstLevel 的多个 secondLevel。
我还尝试用一个对象组成一个新类型,该对象可能是键RequireOnlyOne<keyof MenuItems>
和一个也RequireOnlyOne
用于值的值,但无法实现。
我想要的示例,将所需的类型称为customType
:
const workingObject: customType = {
firstLevel: { // Just one property of the first level
secondLevel: ['one', 'two'] // Just one property of the second level
};
}
const errorObject: customType = {
firstLevel: {
secondLevel: ['one', 'two']
};
anotherFirstLevel: { // Should not work as I am including 2 properties for the first level
anotherSecondLevel: ['one', 'two']
};
}
const anotherErrorObject: customType = {
anotherFirstLevel: {
anotherSecondLevel: ['one', 'two'],
oneMoreSecondLevel: null // Should not work neither as I am including 2 properties for second level
};
}
如果对象具有多个一级属性和/或多个二级属性,则该类型应引发错误。使用建议的RequireOnlyOne
类型,我可以实现这一点,但仅适用于第一级,但我需要第一级和第二级的相同效果。
有任何想法吗?
解决方案
我不知道如何更改RequireOnlyOne
类型,但我知道如何创建新类型。
export interface MenuItems {
firstLevel: {
secondLevel: ['one', 'two']
};
anotherFirstLevel: {
anotherSecondLevel: ['one', 'two']
oneMoreSecondLevel: null
};
}
type Primitives = string | number | boolean | null | undefined | bigint | symbol
type UnionKeys<T> = T extends T ? keyof T : never;
// credits goes to https://stackoverflow.com/questions/65805600/type-union-not-checking-for-excess-properties#answer-65805753
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
type Transform<Obj, Keys extends keyof Obj = keyof Obj, Result = never> =
StrictUnion<
Keys extends string ? { // #1
[Key in Keys]:
Key extends keyof Obj
? (Obj[Key] extends Primitives
? Obj[Key]
: (Obj[Key] extends any[]
? Obj[Key]
: Transform<Obj[Key], keyof Obj[Key], Obj[Key]>)
)
: never
} : Result>
type CustomType = Transform<MenuItems>
const workingObject: CustomType = {
firstLevel: { // Just one property of the first level
secondLevel: ['one', 'two'] // Just one property of the second level
},
}
const errorObject: CustomType = {
firstLevel: {
secondLevel: ['one', 'two']
},
anotherFirstLevel: { // Should not work as I am including 2 properties for the first level
anotherSecondLevel: ['one', 'two']
},
}
const anotherErrorObject: CustomType= {
anotherFirstLevel: {
anotherSecondLevel: ['one', 'two'],
oneMoreSecondLevel: null // Should not work neither as I am including 2 properties for second level
},
}
Transform
- 是主要的实用程序类型。递归迭代键。1# Keys extends string
- 此行确保Keys is distributet
. 这意味着该行之后的整个代码将应用于每个键。请参阅文档以获取更多信息。
我还添加了Obj[Key] extends any[]
- 因为您不希望(我想)遍历数组键。
推荐阅读
- python - Pandas 将 DataTable 加入 SQL 表以防止内存错误
- javascript - p5.sound 手机声音失真?
- css - 在单击集合中的单个元素时应用条件样式
- git - 如何使用 git 更改远程存储库
- google-sheets - 创建一个镜像平方数?
- python - numpy - 通过多个等式动态选择
- python-3.x - 如何“修复”使用 Pandas read_sql_query 进行 sql 查询后返回的对象类型
- c# - 如何按总人数和总价值在工人之间平均分配工作?
- c# - (已解决)如何在函数中创建可用于所有其他函数的变量?
- windows - 如何从以管理员身份运行的更新程序/脚本更新 Windows 任务计划程序任务