awesome-typescript
awesome-typescript copied to clipboard
「重学TS 2.0 」TS 练习题第二十九题
实现一个 ToPath
工具类型,用于把属性访问(.
或 []
)路径转换为元组的形式。具体的使用示例如下所示:
type ToPath<S extends string> = // 你的实现代码
ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
请在下面评论你的答案。
type indexSignature<T> = T extends `${infer H}[${infer M}][${infer R}]`
? [H, M, ...indexSignature<`[${R}]`>]
: T extends `${infer F}[${infer L}]`
? [F, L]
: [T];
type NonSpace<T extends string[]> = T extends [infer H, ...infer R]
? R extends string[]
? H extends ""
? [...NonSpace<R>]
: [H, ...NonSpace<R>]
: never
: T;
type ToPath<S extends string> = S extends `${infer H}.${infer R}`
? [...NonSpace<indexSignature<H>>, ...ToPath<R>]
: NonSpace<indexSignature<S>>;
type t1 = ToPath<"foo.bar.baz">; //=> ['foo', 'bar', 'baz']
type t2 = ToPath<"foo[0][2][3][4].bar[5][6].baz[12][13]">; //=> ['foo','0','2','3','4','bar','5','6','baz','12','13']
import { type } from "os";
type ToPath<S extends string> = S extends `${infer A}.${infer B}` ? [...IndexStructure<A>, ...ToPath<B>] : [...IndexStructure<S>];// 你的实现代码
type IndexStructure<S extends string> = S extends `${infer A}[${infer B}]` ? [A, ...IndexStructure2<`[${B}]`>] : [S]
type IndexStructure2<S extends string> = S extends `[${infer A}][${infer B}]` ? [A, ...IndexStructure2<`[${B}]`>] : S extends `[${infer A}]` ? [A] : [S];
type T1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type T2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type Str2Tuple<S extends string> = S extends `${infer First}[${infer Second}]`
? [First, Second]
: [S];
type ToPath<S extends string> = S extends `${infer First}.${infer Rest}`
? [...Str2Tuple<First>, ...ToPath<Rest>]
: [S];
type T1 = ToPath<"foo.bar.baz">; //=> ['foo', 'bar', 'baz']
type T2 = ToPath<"foo[0].bar.baz">; //=> ['foo', '0', 'bar', 'baz']
感觉我这个方法有点僵硬, 完全就是字符串的替换, 把 '['
替换为 '.'
, ]
替换为 ''
, 然后检查 '.'
前后的子字符串, 存入结果数组
type ToPath<T extends string, C extends string[] = []> =
T extends `${infer R1}[${infer R2}`
? ToPath<`${R1}.${R2}`>
: T extends `${infer R3}]${infer R4}`
? ToPath<`${R3}${R4}`>
: T extends `${infer R5}.${infer R6}`
? [R5, ...ToPath<R6, C>]
: [T]
// 测试用例
type t1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type t2 = ToPath<'foo[0].bar[1].baz[2].fff'> //=> ['foo', '0', 'bar', 'baz']
type ToPath<S extends string> =
S extends '' ? [] :
S extends `${infer A}[${infer B}]${infer C}` ? [...ToPath<A>, B, ...ToPath<C extends `.${infer D}` ? D : C>] : Split<S, '.'>
type T1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type T2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type T3 = ToPath<'foo.bar[0].baz'> //=> ['foo', 'bar', '0', 'baz']
type T4 = ToPath<'foo.bar.baz[0]'> //=> ['foo', 'bar', 'baz', '0']
type T5 = ToPath<'baz[0]'> //=> ['baz', '0']
type T6 = ToPath<'[0].foo[1][3].bar[2]'> //=> ['0','foo','1','3','bar','2']
/**
* 实现一个 ToPath 工具类型,用于把属性访问(. 或 [])路径转换为元组的形式。具体的使用示例如下所示:
* 思路, 需要建立两个匹配分支:
* 1. 匹配 preCode[0]nextCode
* 2. 匹配 preCode.nextCode
* 在匹配到分支1的情况下, preCode 内会存在 可以匹配分支2的情况, 所以需要递归preCode, nextCode也是类似.
* 同时, 匹配到分支2的情况下, preCode 内会存在 可以匹配分支1的情况, 所以需要递归preCode, nextCode也是类似.
*/
type ToPath<S extends string> = S extends
`${infer F}${`[${infer D}]`}${infer R}`
? [...ToPath<F>, ...([D] extends [never] ? [] : [D]), ...ToPath<R>]
: S extends `${infer F}.${infer R}`
? [...ToPath<F>, ...ToPath<R>]
: S extends '' ? [] : [S]
type P1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type P2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type ToPath<S extends string> = S extends `${infer A}.${infer B}`
? [...ToPath<A>, ...ToPath<B>]
: S extends `${infer A}[${infer B}]`
? [A, B]
: [S]
type T1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type T2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
利用 infer 的一些小技巧,结合模板变量的用法。
type CC<T extends string> = T extends `${infer H}${'['}${infer G}${']'}` ? [H, G] : [T]
type ToPath<S extends string> = S extends '' ? [] : S extends `${infer A}${'.'}${infer B}` ? [...CC<A>, ...ToPath<B>] : CC<S>// 你的实现代码
type L0 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type L1 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type L2 = ToPath<'foo[0].bar[1].baz'> //=> ['foo', '0', 'bar','1' ,'baz']
type L3 = ToPath<'foo.bar.baz[0]'> //=> ['foo', 'bar', 'baz','0']
type L4 = ToPath<''> //=> ['foo', '0', 'bar', 'baz']
type ToPath<S extends string> = S extends `${infer A}${"["}${infer C}`
? [A, ...ToPath<C>]
: S extends `${infer A}${"]"}${infer C}`
? [A, ...ToPath<C>]
: S extends `${infer A}${"."}${infer C}`
? (A extends ""
? [...ToPath<C>]
: [A, ...ToPath<C>])
: [S];
type T0 = ToPath<"foo.bar.baz">; //=> ['foo', 'bar', 'baz']
type T1 = ToPath<"foo[0].bar.baz">; //=> ['foo', '0', 'bar', 'baz']
type StrBrackets<S extends string> = S extends `${infer A}[${infer B}]`
? [A, B]
: [S];
type ToPath<S extends string> = S extends `${infer A}.${infer B}`
? [...StrBrackets<A>, ...ToPath<B>]
: [...StrBrackets<S>];
type toPath = ToPath<"foo.bar.baz">; //=> ['foo', 'bar', 'baz']
type toPath2 = ToPath<"foo[0].bar.baz">; //=> ['foo', '0', 'bar', 'baz']
type ToPath<S extends string> = S extends `${infer A}[${infer B}]${infer Rest}` ?
[A, B, ...ToPath<Rest>] :
S extends `${infer C}.${infer D}` ?
C extends '' ? [...ToPath<D>] :
[C, ...ToPath<D>] :
[S];
type A = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type B = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type Split< S extends string, Delimiter extends string, > = S extends `${infer L}${Delimiter}${infer R}` ? [L, ...Split<R, Delimiter>] : [S]; type ToPath<S extends string, A = Split<S, '.'>> = A extends [infer F, ...infer O] ? F extends `${infer Q}[${infer C}]` ? [Q, C, ...ToPath<'', O>] : [F, ...ToPath<'', O>] : A;
type a = ToPath<'foo.bar.baz'>; type b = ToPath<'foo[0].bar.baz'>
type ToPath<S extends string, R extends string[] = []> = S extends `${infer Prev}.${infer Last}` ? Prev extends `${infer Key}[${infer Index}]` ? ToPath<Last, [...R, Key, Index]> : ToPath<Last, [...R, Prev]> : '' extends S ? R : [...R, S];
type A = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type B = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type ToPath<S extends string> = S extends ${infer A}.${infer B}
? [A, ...ToPath<B>] : [S] // 你的实现代码
type TransferStr<T extends Array[${infer A}]${infer B}
?
[A, B] : T[K] extends ${infer B}[${infer A}]
?
[B, A] : T[K] extends ${infer A}[${infer B}]${infer C}
?
[A, B, C] : T[K]
}
type Flat<T extends Array
type Result2 = Flat<TransferStr<ToPath<'foo[0].[0]bar.b[0]az'>>> //=> ["foo", "0", "0", "bar", "b", "0", "az"]
type ToPath<S extends string> = S extends `${infer F}.${infer R}`
? [...ToPath<F>, ...ToPath<R>]
: S extends `${infer F}[${infer C}]${infer R}`
? [...ToPath<F>, ...ToPath<C>, ...ToPath<R>]
: S extends ""
? []
: [S];
可能是比较简洁好懂的写法了
> 每次看到这种需要循环处理的题目第一反应是想到递归type ToPath<S extends string> =
S extends `${infer First}.${infer Rest}`
? First extends `${infer InnerFirst}[${infer Num}]`
? [InnerFirst,Num,...ToPath<Rest>]
: [First,...ToPath<Rest>]
: [S]
type Res1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type Res2 =ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
//question
// 实现一个 ToPath 工具类型,用于把属性访问(. 或 [])路径转换为元组的形式。具体的使用示例如下所示:
// type ToPath<S extends string> = // 你的实现代码
// ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
// ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
//answer
type ToPath<S extends string> = S extends `${infer T}.${infer R}` ? (T extends `${infer F}[${infer A}]` ? [F, A, ...ToPath<R>] : [T, ...ToPath<R>]) : [S]
type a1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type a2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type Split<
S extends string,
Delimiter extends string,
> = S extends `${infer F}${Delimiter}${infer Rest}`
? [F, ...Split<Rest, Delimiter>]
: S extends '' ? [] : [S];
type D<S> = S extends `${infer A}[${infer B}]` ? [A, ...Split<B, ']['>] : [S];
type C<T extends any[]> = T extends [infer F, ...infer Rest] ? [...D<F>, ...C<Rest>] : T;
type ToPath<S extends string> = C<Split<S, '.'>>;
type A = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type B = ToPath<'foo[1].bar[6][7][8].baz[12]'> //=> ["foo", "1", "bar", "6", "7", "8", "baz", "12"]
type ArrayChangeTuple<
S extends string,
TEMP extends string[] = []> = S extends ${infer First}[${infer Center}]${infer Rest}
? First extends ''
? [...TEMP, Center]
: ArrayChangeTuple<Rest, [...TEMP, First, Center]>
: [...TEMP, S];
type ToPath<
S extends string> = S extends ${infer First}.${infer Rest}
? [...ArrayChangeTuple<First>, ...ToPath<Rest>]
: [S]
type ToPath<S extends string> = S extends `${infer K}[${infer M}]${infer R2}` ? [K,...ToPath<`${M}${R2}`>] : S extends `${infer P}.${infer Rest}` ?
[P,...ToPath<Rest>] : [S]
type T1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type T2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
// 拆分
type Split<
S extends string,
Delimiter extends string,
> = S extends `${infer A}${Delimiter}${infer B}` ? [A, ...Split<B, Delimiter>] : [S]
// 将方括号内的类型拆分
type StrSplit<S extends string, R extends string[] = []> =
S extends `${infer A}[${infer B}]${infer C}` ? (C extends "" ? (A extends "" ? [...R, B] : [...R, A, B]) : A extends "" ? [...R, B, ...StrSplit<C, R>] : [...R, A, B, ...StrSplit<C, R>]) : [S]
// 拆分数组每一项
type Spliting<T extends string[], R extends string[] = []> =
T extends [infer FirstStr extends string, ...infer RestStrs extends string[]] ?
(RestStrs extends [] ? [...R, ...StrSplit<FirstStr, R>] : [...R, ...StrSplit<FirstStr, R>, ...Spliting<RestStrs, R>]) : R
type ToPath<S extends string> = Spliting<Split<S, '.'>>
type path1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type path2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type path3 = ToPath<'x.b.c[2][2][3].e.f[2][3][4]'> // => ["x", "b", "c", "2", "2", "3", "e", "f", "2", "3", "4"]
type ToPath<S extends string> = S extends `${infer A}.${infer B}` ?
([...ToPath<A>, ...ToPath<B>]) :
(S extends `${infer E}[${infer F}]${infer G}` ?
(E extends '' ? (G extends '' ? [F] : [F, ...ToPath<G>]) : (G extends '' ? [E, F] : [E, F, ...ToPath<G>])) :
([S]))
type P1 = ToPath<'foo.bar[0].baz'> //=> ['foo', 'bar', 'baz']
type P2 = ToPath<'foo[0].bar[1].baz'> //=> ['foo', '0', 'bar', 'baz']
type P3 = ToPath<'foo[0]'>
type P4 = ToPath<'foo.bar.baz[0]'>
type P5 = ToPath<'foo.bar.baz'>
type P6 = ToPath<'foo'>
type P7 = ToPath<'foo[0][1][3][4][5].baz.bar[0]'>
type p8 = ToPath<'foo[0].bar'>
type P9 = ToPath<'foo.bar[0]'>
type P10 = ToPath<'foo[0].bar.baz'>
type P11 = ToPath<"foo[0][2][3][4].bar[5][6].baz[12][13]">
type P12 = ToPath<'x.b.c[2][2][3].e.f[2][3][4]'>
// 拆分 type Split< S extends string, Delimiter extends string, > = S extends `${infer A}${Delimiter}${infer B}` ? [A, ...Split<B, Delimiter>] : [S] // 将方括号内的类型拆分 type StrSplit<S extends string, R extends string[] = []> = S extends `${infer A}[${infer B}]${infer C}` ? (C extends "" ? (A extends "" ? [...R, B] : [...R, A, B]) : A extends "" ? [...R, B, ...StrSplit<C, R>] : [...R, A, B, ...StrSplit<C, R>]) : [S] // 拆分数组每一项 type Spliting<T extends string[], R extends string[] = []> = T extends [infer FirstStr extends string, ...infer RestStrs extends string[]] ? (RestStrs extends [] ? [...R, ...StrSplit<FirstStr, R>] : [...R, ...StrSplit<FirstStr, R>, ...Spliting<RestStrs, R>]) : R type ToPath<S extends string> = Spliting<Split<S, '.'>> type path1 = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz'] type path2 = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz'] type path3 = ToPath<'x.b.c[2][2][3].e.f[2][3][4]'> // => ["x", "b", "c", "2", "2", "3", "e", "f", "2", "3", "4"]
这个是正确的,评论区好多都有问题,遇到这种 case
:type T3 = ToPath<'fo[1]o[0].b.ar[2].b[3]a.z.'>
结果就不符合预期了
type StrBrackets<S> = S extends `${infer A}[${infer B}]${infer C}`
? [...(A extends '' ? [] : [A]), B, ...StrBrackets<C>]
: (S extends '' ? [] : [S])
type ToPath<S> = S extends `${infer A}.${infer B}`
? [...StrBrackets<A>, ...ToPath<B>]
: StrBrackets<S>
type x = ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
type y = ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
type z = ToPath<'foo[0][1].bar.baz'> //=> ['foo', '0', '1', 'bar', 'baz']
type T3 = ToPath<'fo[1]o[0].b.ar[2].b[3]a.z.'> // ["fo", "1", "o", "0", "b", "ar", "2", "b", "3", "a", "z"]
type path3 = ToPath<'x.b.c[2][2][3].e.f[2][3][4]'> // => ["x", "b", "c", "2", "2", "3", "e", "f", "2", "3", "4"]