awesome-typescript icon indicating copy to clipboard operation
awesome-typescript copied to clipboard

「重学TS 2.0 」TS 练习题第二十九题

Open semlinker opened this issue 2 years ago • 24 comments

实现一个 ToPath 工具类型,用于把属性访问(.[])路径转换为元组的形式。具体的使用示例如下所示:

type ToPath<S extends string> = // 你的实现代码

ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']

请在下面评论你的答案。

semlinker avatar Sep 21 '21 13:09 semlinker

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']

sunboyZgz avatar Sep 21 '21 15:09 sunboyZgz

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']

zerolee1231 avatar Sep 21 '21 16:09 zerolee1231

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']

zhaoxiongfei avatar Sep 22 '21 00:09 zhaoxiongfei

感觉我这个方法有点僵硬, 完全就是字符串的替换, 把 '[' 替换为 '.', ] 替换为 '', 然后检查 '.' 前后的子字符串, 存入结果数组

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']

xiaoYuanDun avatar Sep 23 '21 10:09 xiaoYuanDun

  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']

ln0y avatar Sep 26 '21 08:09 ln0y

/**
 * 实现一个 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']

Jevon617 avatar Sep 28 '21 08:09 Jevon617

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 的一些小技巧,结合模板变量的用法。

zhaoxiongfei avatar Oct 02 '21 10:10 zhaoxiongfei

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']

mingzhans avatar Oct 07 '21 02:10 mingzhans

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']

Gengar-666 avatar Nov 01 '21 08:11 Gengar-666

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']

364 avatar Nov 03 '21 08:11 364

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']

xuemanchi avatar Nov 04 '21 09:11 xuemanchi

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'>

liulise avatar Dec 09 '21 08:12 liulise

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']

xuemanchi avatar Mar 01 '22 12:03 xuemanchi

type ToPath<S extends string> = S extends ${infer A}.${infer B} ? [A, ...ToPath<B>] : [S] // 你的实现代码

type TransferStr<T extends Array = []> = { [K in keyof T]: T[K] extends [${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, Result extends Array = []> = T extends [infer A, ...infer B] ? A extends Array ? Flat<B, [...Result, ...A]> : Flat<B, [...Result, A]> : Result

type Result2 = Flat<TransferStr<ToPath<'foo[0].[0]bar.b[0]az'>>> //=> ["foo", "0", "0", "bar", "b", "0", "az"]

zouyk avatar Mar 07 '22 03:03 zouyk

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];

zjxxxxxxxxx avatar Mar 24 '22 02:03 zjxxxxxxxxx

可能是比较简洁好懂的写法了

> 每次看到这种需要循环处理的题目第一反应是想到递归
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']

heweijian4933 avatar Jun 03 '22 04:06 heweijian4933

//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']

ChuTingzj avatar Jun 09 '22 12:06 ChuTingzj


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"]

GuoJianjian avatar Aug 10 '22 08:08 GuoJianjian

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]

hezhisheng1930 avatar Aug 12 '22 08:08 hezhisheng1930

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']

g1f9 avatar Aug 20 '22 05:08 g1f9

// 拆分
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"]

zhixiaotong avatar Feb 03 '23 08:02 zhixiaotong

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]'>

xxiaojiujiu avatar Feb 08 '23 06:02 xxiaojiujiu

// 拆分
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"]

这个是正确的,评论区好多都有问题,遇到这种 casetype T3 = ToPath<'fo[1]o[0].b.ar[2].b[3]a.z.'> 结果就不符合预期了

SweeperDuan avatar Dec 12 '23 07:12 SweeperDuan

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"]


lfzs avatar Apr 11 '24 09:04 lfzs