TypeScript 杂记八 《Query String Parser》
Query String Parser
思路
type ParserHelper<
T extends string,
R extends Record<string, unknown[]> = {}
> = T extends `${infer A}&${infer B}`
? A extends `${infer K}=${infer V}`
? ParserHelper<B, Merged<R, K, V>>
:
ParserHelper<B, Merged<R, A, true>>
: T extends `${infer K}=${infer V}`
? Merged<R, K, V>
: T extends ""
? R
:
Merged<R, T, true>;
- 合并值
- 考虑到存在多个值的情况,需要采用元组形式。我们这里统一都采用元组形式
- 先不考虑数据重复的情况
- 如果 V 是一个空字符串则将 V 设置成 true
- 将 R[K] 和 V 进行合并
- 将 R 中对应的 K 移除
- 将新的 R 和新的 K V 合并
type Merged<
R extends Record<string, unknown[]>,
K extends keyof R,
V
> = V extends ""
? Merged<R, K, true>
: Omit<R, K> & Record<K, R[K] extends unknown[] ? [...R[K], V] : [V]>;
- 元组中的值去重
- 使用 K 来定义元组中所有的值(不重复,其结果为
"1" | "2" | true
形式) - R 用来存储最终的结果
- 如果 A 在 K 中,则表示重复,继续下一步
- 如果 A 不在 K 中,则表示唯一,新的 K 变成了
K | A
,R 变成了 [...R, A]
- 当 T 为空的时候,返回结果 R。如果 R 里边只有一个值,则返回对应的这个值
type Deduplication<
T extends unknown[],
K = "",
R extends unknown[] = []
> = T extends [infer A, ...infer B]
? A extends K
? Deduplication<B, K, R>
: Deduplication<B, K | A, [...R, A]>
: R extends [infer U]
? U
: R;
完整示例
type Deduplication<
T extends unknown[],
K = "",
R extends unknown[] = []
> = T extends [infer A, ...infer B]
? A extends K
? Deduplication<B, K, R>
: Deduplication<B, K | A, [...R, A]>
: R extends [infer U]
? U
: R;
type Merged<
R extends Record<string, unknown[]>,
K extends keyof R,
V
> = V extends ""
? Merged<R, K, true>
: Omit<R, K> & Record<K, R[K] extends unknown[] ? [...R[K], V] : [V]>;
type ParserHelper<
T extends string,
R extends Record<string, unknown[]> = {}
> = T extends `${infer A}&${infer B}`
? A extends `${infer K}=${infer V}`
? ParserHelper<B, Merged<R, K, V>>
: ParserHelper<B, Merged<R, A, true>>
: T extends `${infer K}=${infer V}`
? Merged<R, K, V>
: T extends ""
? R
: Merged<R, T, true>;
type Parser<T extends Record<string, unknown[]>> = {
[K in keyof T]: Deduplication<T[K]>;
};
type QueryStringParser<T extends string> = Parser<ParserHelper<T>>;
测试
type A =
QueryStringParser<"a=&a=1&a=1&a=1&b=2&c=3&a=1&b=2&c=3&a=1&b=2&c=3&c=3&a=&a=1&b=5&x=12&xxx&aaa&bbb">;
type B = QueryStringParser<"aaasad">;