TypeScript 杂记七 《JSON Parser》
思路
- 先写主类型的大概
- 如果是 {} 包裹则返回 ParserObject
- 如果是 [] 包裹则返回 ParserArray
- 否则返回原本的数据,这里我们额外处理一下 true,false,null
- 不考虑 Numbers 和 Unicode
type Parser<T extends string> = T extends `{${infer S}}`
? ParserObject<S>
: T extends `[${infer S}]`
? ParserArray<S>
: T extends "true"
? true
: T extends "false"
? false
: T extends "null"
? null
: // Numbers and Unicode escape (\uxxxx) in JSON can be ignored. You needn't to parse them.
T;
- 考虑到下边几种情况,因此我们要先处理一下数据
" {"a":1} "
处理前后空格"aaa"
返回的是"\"aaa\""
,因此需要处理一下前后的引号
type TrimBase<
T extends string,
R extends string = " "
> = T extends `${R}${infer S}`
? TrimBase<S, R>
: T extends `${infer S}${R}`
? TrimBase<S, R>
: T;
// 处理空格->处理引号
type Trim<T extends string> = TrimBase<TrimBase<T>, '"'>;
type ParserHelper<T extends string> = T extends `{${infer S}}`
? ParserObject<Trim<S>> // 特殊解决一下 { } 这种情况
: T extends `[${infer S}]`
? ParserArray<S>
: T extends "true"
? true
: T extends "false"
? false
: T extends "null"
? null
: // Numbers and Unicode escape (\uxxxx) in JSON can be ignored. You needn't to parse them.
T;
type Parser<T extends string> = ParserHelper<Trim<T>>;
-
采用递归的形式
- 先解析出最一层的数据,拿到 key 和 value
- value 则再次调用 Parser
- 重复第一步
-
ParserArray
的实现- 传入的数据不带去掉最外层的 []
- 理论上对于一个普通数字,遇到
,
则前边的内容的表示是一项 - 但是因为有可能是一个对象或者数组我们需要额外处理一下
- V 用来临时存储数据
- L 用来存储遇到的 [ { 个数
- 如果遇到 [ 或者 { 则 L 数组加一
- 如果遇到 ] 或者 } 则 L 数组减一
- 如果遇到 , 且 L 数组为空,则表示一项完成了,把 V 存到 R 里边,继续下一项
- 否则就把对应的字符加入到 V 中
- 注意:存入的 V 值,需要递归去调用
Parser
type ParserArray<
T extends string,
R extends string[] = [],
V extends string = "",
L extends unknown[] = []
> = T extends `${infer S}${infer E}`
? S extends ","
? L extends []
? ParserArray<E, [...R, Parser<V>], "", L>
: ParserArray<E, R, `${V}${S}`, L>
: S extends "[" | "{"
? ParserArray<E, R, `${V}${S}`, [...L, unknown]>
: S extends "}" | "]"
? L extends [unknown, ...infer B]
? ParserArray<E, R, `${V}${S}`, B>
: ParserArray<E, R, `${V}${S}`, L>
: ParserArray<E, R, `${V}${S}`, L>
: [...R, Parser<V>];
ParserObject
的实现- 不同的是我们需要两个参数,用来存储 K 和 V
- L=false 获取 K,只要遇到 : 就停止前边的内容就是对应的 key
- L=true 获取值,这里的逻辑就和数组是一样的,不同的是这里只求一项
- 求出 value 的时候,我们把 K 和 V 存到 R 中,重新开始下一项
type ParserObject<
T extends string,
R extends Record<string, any> = {},
K extends string = "",
V extends string = "",
F extends boolean = false,
L extends unknown[] = []
> = T extends `${infer S}${infer E}`
? F extends false
? S extends ":"
? ParserObject<E, R, Trim<K>, V, true, L>
: ParserObject<E, R, `${K}${S}`, V, F, L>
: S extends ","
? L extends []
? ParserObject<
E,
{ [P in keyof R]: R[P] } & { [P in K]: Parser<V> },
"",
"",
false,
L
>
: ParserObject<E, R, K, `${V}${S}`, F, L>
: S extends "[" | "{"
? ParserObject<E, R, K, `${V}${S}`, F, [...L, unknown]>
: S extends "]" | "}"
? L extends [unknown, ...infer B]
? ParserObject<E, R, K, `${V}${S}`, F, B>
: ParserObject<E, R, K, `${V}${S}`, F, L>
: ParserObject<E, R, K, `${V}${S}`, F, L>
: K extends ""
? { [P in keyof R]: R[P] }
: ParserObject<
"",
{ [P in keyof R]: R[P] } & { [P in K]: Parser<V> },
"",
"",
false,
L
>;
完整示例
type TrimBase<
T extends string,
R extends string = " "
> = T extends `${R}${infer S}`
? TrimBase<S, R>
: T extends `${infer S}${R}`
? TrimBase<S, R>
: T;
type Trim<T extends string> = TrimBase<TrimBase<T>, '"'>;
type ParserObject<
T extends string,
R extends Record<string, any> = {},
K extends string = "",
V extends string = "",
F extends boolean = false,
L extends unknown[] = []
> = T extends `${infer S}${infer E}`
? F extends false
? S extends ":"
? ParserObject<E, R, Trim<K>, V, true, L>
: ParserObject<E, R, `${K}${S}`, V, F, L>
: S extends ","
? L extends []
? ParserObject<
E,
{ [P in keyof R]: R[P] } & { [P in K]: Parser<V> },
"",
"",
false,
L
>
: ParserObject<E, R, K, `${V}${S}`, F, L>
: S extends "[" | "{"
? ParserObject<E, R, K, `${V}${S}`, F, [...L, unknown]>
: S extends "]" | "}"
? L extends [unknown, ...infer B]
? ParserObject<E, R, K, `${V}${S}`, F, B>
: ParserObject<E, R, K, `${V}${S}`, F, L>
: ParserObject<E, R, K, `${V}${S}`, F, L>
: K extends ""
? { [P in keyof R]: R[P] }
: ParserObject<
"",
{ [P in keyof R]: R[P] } & { [P in K]: Parser<V> },
"",
"",
false,
L
>;
type ParserArray<
T extends string,
R extends string[] = [],
V extends string = "",
L extends unknown[] = []
> = T extends `${infer S}${infer E}`
? S extends ","
? L extends []
? ParserArray<E, [...R, Parser<V>], "", L>
: ParserArray<E, R, `${V}${S}`, L>
: S extends "[" | "{"
? ParserArray<E, R, `${V}${S}`, [...L, unknown]>
: S extends "}" | "]"
? L extends [unknown, ...infer B]
? ParserArray<E, R, `${V}${S}`, B>
: ParserArray<E, R, `${V}${S}`, L>
: ParserArray<E, R, `${V}${S}`, L>
: [...R, Parser<V>];
type ParserHelper<T extends string> = T extends `{${infer S}}`
? ParserObject<Trim<S>>
: T extends `[${infer S}]`
? ParserArray<S>
: T extends "true"
? true
: T extends "false"
? false
: T extends "null"
? null
: // Numbers and Unicode escape (\uxxxx) in JSON can be ignored. You needn't to parse them.
T;
type Parser<T extends string> = ParserHelper<Trim<T>>;
测试
type A =
Parser<' { "asdasd ":{} , " a123 " : true,"b123":{"b1acads": 1," b2":{"aasd ":false},"b3asda":2},"c":3,"d":{"d":null}}'>;
// type A = {
// 'asdasd ': {}
// ' a123 ': true
// b123: {
// b1acads: '1'
// ' b2': {
// 'aasd ': false
// }
// b3asda: '2'
// }
// c: '3'
// d: {
// d: null
// }
// }
type B = Parser<'[ 1 ,true, false,null ,{ },{ " a ":1,"b":{"b":"2"}},2,3]'>;
// type B = [
// '1',
// true,
// false,
// null,
// {},
// {
// 'a ': '1'
// b: {
// b: '2'
// }
// },
// '2',
// '3',
// ]