英文:
how to merge ...infer and some other type in typescript
问题
我正在使用 TypeScript 对验证器进行类型安排。例如,"required" 和 "noRequired" 是互斥的逻辑。如果使用了 "required",则不需要再使用 "required" 或 "noRequired"。您可以通过复制以下代码并在 VS Code 中使用对象 "b" 进行调试。
type CoFormRuleClarity = {
email(): any;
id(): any;
};
type CoFormRuleSize = {
min(num: number): any;
max(num: number): any;
between(min: number, max: number): any;
};
type CoFormRuleRequire = {
required(): any;
noRequired(): any;
};
type InvalideFunctionParams = "请传递函数参数";
type CoFormRuleWarpKeys<T, Raw> = {
[Key in keyof T]: T[Key] extends (...args: infer P) => any
? (
...args: P
) => Raw extends [T, ...(infer Right)]
? CoFormRuleMerge<Right, Raw>
: Raw extends [...(infer Left), T]
? CoFormRuleMerge<Left, Raw>
: Raw extends [
infer Left1,
...(infer Left2),
T,
...(infer Right1),
infer Right2
]
// 主要部分在这里
? CoFormRuleMerge<[Left1, ...Left2, ...Right1, Right2], Raw>
: InvalideFunctionParams
: InvalideFunctionParams;
};
type CoFormRuleMerge<T, Raw> = T extends [infer Left, ...(infer Right)]
? CoFormRuleWarpKeys<Left, Raw> & CoFormRuleMerge<Right, Raw>
: T extends [infer Left]
? CoFormRuleWarpKeys<Left, Raw>
: T extends [infer Left, infer Right]
? CoFormRuleWarpKeys<Left, Raw> & CoFormRuleWarpKeys<Right, Raw>
: {};
type CoFormRuleMergeCall<T extends unknown[]> = CoFormRuleMerge<T, T>;
const a: CoFormRuleMergeCall<[
CoFormRuleRequire,
CoFormRuleSize,
CoFormRuleClarity
]> = {} as any;
// 示例 1
const b = a.between(1, 2);
// b. 当前: CoFormRuleRequire
// b. 期望: CoFormRuleRequire & CoFormRuleClarity
// 示例 2
const c = a.between(1, 2).required();
// c. 当前: CoFormRuleSize & CoFormRuleClarity
// c. 期望: CoFormRuleClarity
感谢您提供的解决方案和优化方案。
英文:
I'm using typescript to do the type arrangement on validators.
For example, required, noRequired are mutually exclusive logic.
If required is used, then neither required nor noRequired needs to be used again.
You can debug this by copying the following code and using the b object in the vs code.
type CoFormRuleClarity = {
email(): any;
id(): any;
};
type CoFormRuleSize = {
min(num: number): any;
max(num: number): any;
between(min: number, max: number): any;
};
type CoFormRuleRequire = {
required(): any;
noRequired(): any;
};
type InvalideFunctionParams = "Please pass the function parameters";
type CoFormRuleWarpKeys<T, Raw> = {
[Key in keyof T]: T[Key] extends (...args: infer P) => any
? (
...args: P
) => Raw extends [T, ...(infer Right)]
? CoFormRuleMerge<Right, Raw>
: Raw extends [...(infer Left), T]
? CoFormRuleMerge<Left, Raw>
: Raw extends [
infer Left1,
...(infer Left2),
T,
...(infer Right1),
infer Right2
]
// the principal part in there
? CoFormRuleMerge<[Left1, ...Left2, ...Right1, Right2], Raw>
: InvalideFunctionParams
: InvalideFunctionParams;
};
type CoFormRuleMerge<T, Raw> = T extends [infer Left, ...(infer Right)]
? CoFormRuleWarpKeys<Left, Raw> & CoFormRuleMerge<Right, Raw>
: T extends [infer Left]
? CoFormRuleWarpKeys<Left, Raw>
: T extends [infer Left, infer Right]
? CoFormRuleWarpKeys<Left, Raw> & CoFormRuleWarpKeys<Right, Raw>
: {};
type CoFormRuleMergeCall<T extends unknown[]> = CoFormRuleMerge<T, T>;
const a: CoFormRuleMergeCall<[
CoFormRuleRequire,
CoFormRuleSize,
CoFormRuleClarity
]> = {} as any;
// example 1
const b = a.between(1, 2);
// b. current: CoFormRuleRequire
// b. expect: CoFormRuleRequire & CoFormRuleClarity
// example 2
const c = a.between(1, 2).required();
// b. current: CoFormRuleSize & CoFormRuleClarity
// b. expect: CoFormRuleClarity
thank you very much for the solution and the optimization solution.
答案1
得分: 0
以下是翻译的内容:
代替将它们操作为元组,您应该考虑使用联合类型。在这样做时,我们首先需要获得一个类型,可以将联合类型 (A | B | C
) 转换为交集类型 (A & B & C
)。幸运的是,这已经由 jcalz 在 这里 中解决:
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
然后,我们可以编写您的 CoFormRuleMergeCall
类型如下:
type CoFormRuleMergeCall<T extends unknown[]> = CoFormRuleMerge<T[number]>;
它仍然接受一个元组(甚至是数组),并将其元素的类型传递给 CoFormRuleMerge
作为带有 T[number]
的联合类型。例如,如果 T
是 [A, B, C]
,则 T[number]
将是 A | B | C
(对于 (A | B | C)[]
也是如此)。您可以将 T[number]
视为将元组/数组展开为其元素类型。
有了这个联合类型,您可以获取该联合类型的交集的键,并将其映射到正确的函数类型:
type CoFormRuleMerge<T> = {
[K in keyof UnionToIntersection<T>]: (...args: Parameters<Extract<T, Record<K, any>>[K]>) => CoFormRuleMerge<Exclude<T, Record<K, any>>>;
};
对于参数,我们从 T
的原始成员中提取,对于返回类型,我们通过排除具有当前键的成员,使用 CoFormRuleMerge
处理 T
的其余成员。
这适用于您提供的代码:
// 示例 1
const b = a.between(1, 2);
// ^? CoFormRuleMerge<CoFormRuleClarity | CoFormRuleRequire>;
// 示例 2
const c = a.between(1, 2).required();
// ^? CoFormRuleMerge<CoFormRuleClarity>;
但是,一旦您使用完所有规则,您将获得 never
:
const d = a.between(1, 2).required().email();
// ^? CoFormRuleMerge<never>
// 与 `never` 相同
英文:
Instead of manipulating them as tuples, you should think about using unions. When doing so, we should first get a type that can turn a union (A | B | C
) into an intersection (A & B & C
). Luckily, that is already covered by jcalz here:
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
Then, we can write your CoFormRuleMergeCall
type as the following:
type CoFormRuleMergeCall<T extends unknown[]> = CoFormRuleMerge<T[number]>;
It still takes a tuple (or even array), and passes the types of its elements to CoFormRuleMerge
as a union with T[number]
. For example, if T
was [A, B, C]
, T[number]
would be A | B | C
(the same is true for (A | B | C)[]
). You can think of T[number]
as "unwrapping" the tuple/array into its element type.
Given this union, you can then take the keys of the intersection of this union, and map it to the correct function type:
type CoFormRuleMerge<T> = {
[K in keyof UnionToIntersection<T>]: (...args: Parameters<Extract<T, Record<K, any>>[K]>) => CoFormRuleMerge<Exclude<T, Record<K, any>>>;
};
For the parameters, we extract the original member of T
, and for the return type, we use CoFormRuleMerge
on the remaining members of T
by excluding the member with the current key.
This works for your provided code:
// example 1
const b = a.between(1, 2);
// ^? CoFormRuleMerge<CoFormRuleClarity | CoFormRuleRequire>
// example 2
const c = a.between(1, 2).required();
// ^? CoFormRuleMerge<CoFormRuleClarity>
However, once you have used up all the rules, you will get never
:
const d = a.between(1, 2).required().email();
// ^? CoFormRuleMerge<never>
// same as `never`
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论