forked from ascoders/weekly
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
179 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
解决 TS 问题的最好办法就是多练,这次解读 [type-challenges](https://github.com/type-challenges/type-challenges) Medium 难度 57~62 题。 | ||
|
||
## 精读 | ||
|
||
### [Trim Right](https://github.com/type-challenges/type-challenges/blob/main/questions/04803-medium-trim-right/README.md) | ||
|
||
实现 `TrimRight` 删除右侧空格: | ||
|
||
```ts | ||
type Trimed = TrimRight<' Hello World '> // expected to be ' Hello World' | ||
``` | ||
用 `infer` 找出空格前的字符串递归一下即可: | ||
```ts | ||
type TrimRight<S extends string> = S extends `${infer R}${' '}` | ||
? TrimRight<R> | ||
: S | ||
``` | ||
再补上测试用例的边界情况,`\n` 与 `\t` 后就是完整答案了: | ||
```ts | ||
// 本题答案 | ||
type TrimRight<S extends string> = S extends `${infer R}${' ' | '\n' | '\t'}` | ||
? TrimRight<R> | ||
: S | ||
``` | ||
### [Without](https://github.com/type-challenges/type-challenges/blob/main/questions/05117-medium-without/README.md) | ||
实现 `Without<T, U>`,从数组 `T` 中移除 `U` 中元素: | ||
```ts | ||
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 的方式解决该问题: | ||
```ts | ||
type ToUnion<T> = T extends any[] ? T[number] : T | ||
``` | ||
这样无论是数字还是数组,都会转成联合类型,而联合类型很方便判断 `extends` 包含关系: | ||
```ts | ||
// 本题答案 | ||
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](https://github.com/type-challenges/type-challenges/blob/main/questions/05140-medium-trunc/README.md) | ||
实现 `Math.trunc` 相同功能的函数 `Trunc`: | ||
```ts | ||
type A = Trunc<12.34> // 12 | ||
``` | ||
如果入参是字符串就很简单了: | ||
```ts | ||
type Trunc<T> = T extends `${infer H}.${infer R}` ? H : '' | ||
``` | ||
如果不是字符串,将其转换为字符串即可: | ||
```ts | ||
// 本题答案 | ||
type Trunc<T extends string | number> = `${T}` extends `${infer H}.${infer R}` | ||
? H | ||
: `${T}` | ||
``` | ||
### [IndexOf](https://github.com/type-challenges/type-challenges/blob/main/questions/05153-medium-indexof/README.md) | ||
实现 `IndexOf` 寻找元素所在下标,找不到返回 `-1`: | ||
```ts | ||
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 | ||
``` | ||
|
||
需要用一个辅助变量存储命中下标,递归的方式一个个判断是否匹配: | ||
|
||
```ts | ||
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` 函数判断相等: | ||
```ts | ||
// 本题答案 | ||
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](https://github.com/type-challenges/type-challenges/blob/main/questions/05310-medium-join/README.md) | ||
实现 TS 版 `Join<T, P>`: | ||
```ts | ||
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` 每次拿第一个元素,再使用一个辅助字符串存储答案,拼接起来即可: | ||
```ts | ||
// 本题答案 | ||
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](https://github.com/type-challenges/type-challenges/blob/main/questions/05317-medium-lastindexof/README.md) | ||
实现 `LastIndexOf` 寻找最后一个匹配的下标: | ||
```ts | ||
type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2> // 3 | ||
type Res2 = LastIndexOf<[0, 0, 0], 2> // -1 | ||
``` | ||
和 `IndexOf` 类似,从最后一个下标往前判断即可。需要注意的是,我们无法用常规办法把 `Index` 下标减一,但好在 `R` 数组长度可以代替当前下标: | ||
```ts | ||
// 本题答案 | ||
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](https://github.com/dt-fe/weekly/issues/433) | ||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。** | ||
> 关注 **前端精读微信公众号** | ||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg"> | ||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters