semlinker/awesome-typescript

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

Opened this issue · 15 comments

实现一个 Split 工具类型,根据给定的分隔符(Delimiter)对包含分隔符的字符串进行切割。可用于定义 String.prototype.split 方法的返回值类型。具体的使用示例如下所示:

type Item = 'semlinker,lolo,kakuqo';

type Split<
	S extends string, 
	Delimiter extends string,
> = // 你的实现代码

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]

请在下面评论你的答案。

suica commented
type Item = 'semlinker,lolo,kakuqo';

type Split<
	S extends string, 
	Delimiter extends string,
> = S extends `${infer A}${Delimiter}${infer B}` ? [A,...Split<B,Delimiter>] : [S];

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]
type Item = "semlinker,lolo,kakuqo";

type Split<
  S extends string,
  Delimiter extends string,
> = S extends `${infer First}${Delimiter}${infer Rest}` ? [First, ...Split<Rest, Delimiter>] : [S];

type ElementType = Split<Item, ",">; // ["semlinker", "lolo", "kakuqo"]

和 29 题一样

type Split<
  S extends string, 
  Delimiter extends string,
  R extends any[] = []   //  记录遍历记录
> = 
S extends `${infer R1}${Delimiter}${infer R2}` 
  ? Split<R2, Delimiter, [...R, R1]>   //  记录R1, 继续遍历剩余部分
  : [...R, S]                          //  没有更多的 Delimiter 了, 直接记录当前的 S 并返回

type ElementType1 = Split<'semlinker,lolo,kakuqo', ','>; // ["semlinker", "lolo", "kakuqo"]
type ElementType2 = Split<'dog=1&cat=2&pig=34', '&'>; // ["dog=1", "cat=2", "pig=34"]

题解

这题只要会用 ${infer X}${S}${infer Y} 这种写法就很方便了,意思是将一个字符串做拆解:

type Item = 'semlinker,lolo';

type Test<
	S extends string, 
	Delimiter extends string,
> = S extends `${infer Key}${Delimiter}${infer Rest}` 
  ? [Key, Rest] 
  : [S]

type TestElement = Test<Item, ','>; // ["semlinker", "lolo"]

这边 Key 对应 "semlinker"Delimiter 对应 ,Rest 对应 "lolo",这个理解了,再看这题解法就比较简单了,也是使用递归:

type Item = 'semlinker,lolo,kakuqo';

type Split<
	S extends string, 
	Delimiter extends string,
> = S extends `${infer Key}${Delimiter}${infer Rest}` 
  ? [Key, ...Split<Rest, Delimiter>] 
  : [S]

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]
type ElementType2 = Split<'a|b|c||d', '|'>; // ["a", "b", "c", "", "d"]
type ElementType3 = Split<'abcdef', ''>; // ["a", "b", "c", "d", "e", "f", ""]

这边细心的朋友会发现 ElementType3 的值最后多出了 "",因为在最后一个位置,取到的 Rest 是空字符串,所以这边我们就需要再做处理,过滤掉空字符串:

type Split<
	S extends string, 
	Delimiter extends string,
> = S extends `${infer Key}${Delimiter}${infer Rest}` 
  ? [Key, ...Split<Rest, Delimiter>] 
  : S extends ''   /* 处理空字符串 */
  ? [] 
  : [S]

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]
type ElementType2 = Split<'a|b|c||d', '|'>; // ["a", "b", "c", "", "d"]
type ElementType3 = Split<'abcdef', ''>; // ["a", "b", "c", "d", "e", "f"]

这样就能去掉最后一个 "" 了。

type Item = 'semlinker,lolo,kakuqo';

type Split<
	S extends string,
	Delimiter extends string,
> = S extends `${infer A}${Delimiter}${infer B}`
  ? [A, ...Split<B, Delimiter>]
  : [S];

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"

知识点: ts 映射类型里可以使用js里的模板变量的语法,用法和含义都相同
思路: 熟练掌握 extends 配合 infer的用法

type ElementType3 = Split<'abcdef',

Split<'a,b,', ','> 这个期望结果是否符合预期

type Split<
	S extends string, 
	Delimiter extends string,
> = S extends `${infer A}${Delimiter}${infer B}` ? [A, ...Split<B, Delimiter>] : [S]

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]

type Split<
S extends string,
Delimiter extends string,
> = S extends `${infer L}${Delimiter}${infer R}`
? [L, ...Split<R, Delimiter>]
: [S];

type Split<S extends string, Delimiter extends string> = S extends `${infer Key}${Delimiter}${infer Rest}`
  ? [Key, ...Split3<Rest, Delimiter>]
  : S extends ''
    ? Delimiter extends ''
      ? []
      : [S]
    : [S];

type A3 = Split3<'', ''> // []
type B3 = Split3<'o', ''> // ['o']
type C3 = Split3<'o', 'o'> // ['','']
zouyk commented

const strDome = 'semlinker,lolo,kakuqo,ahhah'
type Item = typeof strDome;

type Split<
S extends string,
Delimiter extends string,
> = S extends ${infer A},${infer B} ? [A, ...Split<B, Delimiter>] : [S]

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]

const reuslt: Split<Item, ','> = String.prototype.split.call(strDome, ',')

type Item = 'semlinker,lolo,kakuqo';

type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]

type Split<
	S extends string, 
	Delimiter extends string,
> = 
  S extends `${infer Left}${Delimiter}${infer Right}`
    ? [Left, ...Split<Right, Delimiter>]
    : [S]
type Split<
	S extends string, 
	Delimiter extends string,
  Arr extends string[] = []
> = 
  S extends `${infer Left}${Delimiter}${infer Right}`
    ? Split<Right, Delimiter, [...Arr, Left]>
    : [...Arr, S]

type Split<
S extends string,
Delimiter extends string,
TEMP extends string[] = []
> = S extends ${infer First}${Delimiter}${infer Rest}
? Split<Rest, Delimiter, [...TEMP, First]>
: S extends ''
? TEMP : [...TEMP, S]

type Split<
	S extends string, 
	Delimiter extends string,
> = S extends `${infer A}${Delimiter}${infer B}` ? [A, ...Split<B, Delimiter>] : [S]
type Split<
	S extends string, 
	Delimiter extends string,
> = S extends `${infer A}${Delimiter}${infer Rest}`
  ? [A, ...Split<Rest, Delimiter>]
  : [S];