乐趣区

关于javascript:精读Trim-Right-Without-Trunc

解决 TS 问题的最好方法就是多练,这次解读 type-challenges Medium 难度 57~62 题。

精读

Trim Right

实现 TrimRight 删除右侧空格:

type Trimed = TrimRight<'Hello World'> // expected to be 'Hello World'

infer 找出空格前的字符串递归一下即可:

type TrimRight<S extends string> = S extends `${infer R}${' '}`
  ? TrimRight<R>
  : S

再补上测试用例的边界状况,\n\t 后就是残缺答案了:

// 本题答案
type TrimRight<S extends string> = S extends `${infer R}${'' |'\n'|'\t'}`
  ? TrimRight<R>
  : S

Without

实现 Without<T, U>,从数组 T 中移除 U 中元素:

type Res = Without<[1, 2], 1> // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]> // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]> // expected to be []

该题最难的点在于,参数 U 可能是字符串或字符串数组,咱们要判断是否存在只能用 extends,这样就存在两个问题:

  1. 既是字符串又是数组如何判断,合在一起判断还是离开判断?
  2. [1] extends [1, 2] 为假,数组模式如何判断?

能够用数组转 Union 的形式解决该问题:

type ToUnion<T> = T extends any[] ? T[number] : T

这样无论是数字还是数组,都会转成联结类型,而联结类型很不便判断 extends 蕴含关系:

// 本题答案
type Without<T, U> = T extends [infer H, ...infer R]
  ? H extends ToUnion<U>
    ? Without<R, U>
    : [H, ...Without<R, U>]
  : []

每次取数组第一项,判断是否被 U 蕴含,是的话就抛弃(抛弃的动作是把 H 摈弃持续递归),否则蕴含(蕴含的动作是造成新的数组 [H, ...] 并把递归内容解构塞到前面)。

Trunc

实现 Math.trunc 雷同性能的函数 Trunc:

type A = Trunc<12.34> // 12

如果入参是字符串就很简略了:

type Trunc<T> = T extends `${infer H}.${infer R}` ? H : ''

如果不是字符串,将其转换为字符串即可:

// 本题答案
type Trunc<T extends string | number> = `${T}` extends `${infer H}.${infer R}`
  ? H
  : `${T}`

IndexOf

实现 IndexOf 寻找元素所在下标,找不到返回 -1

type Res = IndexOf<[1, 2, 3], 2>; // expected to be 1
type Res1 = IndexOf<[2,6, 3,8,4,1,7, 3,9], 3>; // expected to be 2
type Res2 = IndexOf<[0, 0, 0], 2>; // expected to be -1

须要用一个辅助变量存储命中下标,递归的形式一个个判断是否匹配:

type IndexOf<T, U, Index extends any[] = []> = 
  T extends [infer F, ...infer R]
    ? F extends U
      ? Index['length']
      : IndexOf<R, U, [...Index, 0]>
    : -1

但没有通过测试用例 IndexOf<[string, 1, number, 'a'], number>,起因是 1 extends number 后果为真,所以咱们要换成 Equal 函数判断相等:

// 本题答案
type IndexOf<T, U, Index extends any[] = []> = 
  T extends [infer F, ...infer R]
    ? Equal<F, U> extends true
      ? Index['length']
      : IndexOf<R, U, [...Index, 0]>
    : -1

Join

实现 TS 版 Join<T, P>:

type Res = Join<["a", "p", "p", "l", "e"], "-">; // expected to be 'a-p-p-l-e'
type Res1 = Join<["Hello", "World"], "">; // expected to be'Hello World'type Res2 = Join<["2","2","2"], 1>; // expected to be'21212'type Res3 = Join<["o"],"u">; // expected to be'o'

递归 T 每次拿第一个元素,再应用一个辅助字符串存储答案,拼接起来即可:

// 本题答案
type Join<T, U extends string | number> =
  T extends [infer F extends string, ...infer R extends string[]]
  ? R['length'] extends 0
    ? F
    : `${F}${U}${Join<R, U>}`
  : '' 

惟一要留神的是解决到最初一项时,不要再追加 U 了,能够通过 R['length'] extends 0 来判断。

LastIndexOf

实现 LastIndexOf 寻找最初一个匹配的下标:

type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2> // 3
type Res2 = LastIndexOf<[0, 0, 0], 2> // -1

IndexOf 相似,从最初一个下标往前判断即可。须要留神的是,咱们无奈用惯例方法把 Index 下标减一,但好在 R 数组长度能够代替以后下标:

// 本题答案
type LastIndexOf<T, U> = T extends [...infer R, infer L]
  ? Equal<L, U> extends true
    ? R['length']
    : LastIndexOf<R, U>
  : -1

总结

本周六道题都没有刷到新知识点,中等难题还剩 6 道,如果学到这里能有种索然无味的感觉,阐明后面学习的很扎实。

探讨地址是:精读《Trim Right, Without, Trunc…》· Issue #433 · dt-fe/weekly

如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。

关注 前端精读微信公众号

<img width=200 src=”https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg”>

版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)

退出移动版