惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Darknet – Hacking Tools, Hacker News & Cyber Security
N
News and Events Feed by Topic
N
News | PayPal Newsroom
SecWiki News
SecWiki News
P
Privacy International News Feed
T
Troy Hunt's Blog
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
L
LINUX DO - 热门话题
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Security Latest
Security Latest
AWS News Blog
AWS News Blog
S
Secure Thoughts
W
WeLiveSecurity
H
Heimdal Security Blog
T
Threat Research - Cisco Blogs
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security @ Cisco Blogs
G
GRAHAM CLULEY
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
S
Security Affairs
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google Online Security Blog
Google Online Security Blog
Cisco Talos Blog
Cisco Talos Blog
雷峰网
雷峰网
Cloudbric
Cloudbric
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
Latest news
Latest news
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
D
Docker
Recent Announcements
Recent Announcements
博客园 - 【当耐特】
H
Help Net Security
博客园 - 司徒正美
TaoSecurity Blog
TaoSecurity Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Check Point Blog
博客园 - 叶小钗

AirCloud 日常

快速生成 AI 应用的框架对比:Gradio、Streamlit 和 Dash Node.js 二进制打包:pkg 原理解析 - AirCloud 日常 基于 K8s 容器化运行 web 端对端测试 关于 JupyterLab cell 输出保持问题的设计方案 - AirCloud 日常 使用 Rust 开发 python 模块 基于 Container 架构的多框架前端应用 - AirCloud 日常 我们应该如何进行 Code Review ? - AirCloud 日常 Clean Code》读书笔记 - AirCloud 日常 使用 fst-json 自动生成更快的 json 序列化方法 《代码精进之路》与《代码整洁之道》-读书笔记 - AirCloud 日常 《重构》-读书笔记 - AirCloud 日常 《架构整洁之道》-读书笔记 - AirCloud 日常 React Conf 2021 内容概要 - AirCloud 日常 PC 开发技术选型:Electron 不是银弹 - AirCloud 日常 web 页面内存分析与生产环境禁用 console - AirCloud 日常 通过优化合成层优化性能 - AirCloud 日常 了解 StackOverFlow 上面最受欢迎的语言 Rust - AirCloud 日常 一些性能相关的 JavaScript 代码编写建议规范 - AirCloud 日常 如何让你的精力更多的用于提高技术深度 - AirCloud 日常
读完 type-challenges,我总结了如下常用的内容 - AirCloud 日常
2022-07-30 · via AirCloud 日常

近期用了一定时间通读了 type-challenges,感慨 Typescript 也可以写的如此复杂之余,也出于以下几个原因,对题目做了整理,形成本文:

  • type-challenges 整体题目较多,而且很多时候我们只是学习为主,并不是以“做题”为目的,这样我们在列表-问题-答案列表-答案详情中跳来跳去很麻烦,也很难甄别优质解答。
  • type-challenges 仓库中某些题目,实属偏门,可能对于大多数业务开发来说,永远也用不到,投入时间在这部分,ROI 就会非常低。
  • type-challenges 有些题目比较类似,但是却放到了不同的地方,甚至难度不同,笔者认为结合起来一起看可能更高效。

因此,本文对笔者认为重要的、有业务使用场景或者可以给我们以很大启发的题目进行罗列和解析,同时规避了一些复杂度很高,但实际大多数人用不到的题目(比如 Typescript 实现 JSON 解析),对于普通的 Typescript 开发者而言,看完本文的题目就基本能够以一个比较小的代价掌握 type-challenges 中贴近日常开发和业务的部分。

感谢 type-challenges 仓库的贡献者们

Typescript 内置工具函数

实际上,Typescript 自身除了定义类型以外,自己内置了很多非常有用的工具函数,这部分是我们日常 Typescript 开发应当必须掌握,信手拈来的,我建议如果对这部分不熟悉的话,先多读几遍这部分。

这部分内容,可以在这里 了解,也可以在自己的项目中,点进 node_modules/typescript/lib/lib.es5.d.ts 直接了解,注释比较详尽。

实际上,type-challenges 的一部分简单和中等的题目就是实现这些内置工具函数的二次实现,这部分内容我在本文基本没有重复罗列,但是建议大家去阅读官方实现,一般来说也都比较短小,容易理解

数组第一个元素

原文地址

使用:

1
2
type arr1 = ['a', 'b', 'c']
type head1 = First<arr1>

实现:

1
2
3
export type First<T extends any[]> = T extends [infer first, ...any[]]
? first
: never;

除了本题目,还有许多其他类似的实现::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

export type Last<T extends any[]> = T extends [...any, infer L] ? L : never;




export type Pop<T extends any> = T extends [...infer P, infer R] ? P : never;



export type Push<T extends any[], U> = [...T, U];



export type UnShift<T extends any[], U> = [U, ...T];



export type Shift<T> = T extends [infer F,... infer R] ? R : undefined



export type Concat <T extends any[], V extends any[]> = [...T, ...V];



export type Reverse<T> = T extends [infer Head, ...infer Rest] ? [...Reverse<Rest>, Head] : T



type Filtered = FilterOut<[1, 2, null, 3], null>
export type FilterOut<T extends any[], F> =
T extends [infer First, ...infer Rest]
? First extends F
? FilterOut<Rest, F>
: [First, ...FilterOut<Rest, F>]
: []

技巧提示:

  • 通过 extends 加三目运算符,完成条件判断。
  • 通过类似 infer first, ...any[] 或者 [infer First, ...infer Rest] 这种方式来展开数组类型。
  • 另外我们还可以使用 [...T, U] 来扩充数组的类型定义。

Tuple to Union

原文地址

使用:

1
2
type Arr = ['1', '2', '3']
type Test = TupleToUnion<Arr>

实现比较简单:

1
export type TupleToUnion<T extends any[]> = T[number];

技巧提示:

  • T[number] 这种语法可以比较方便地实现 Tuple to Union

Deep Readonly

原文地址

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type X = { 
x: {
a: 1
b: 'hi'
}
y: 'hey'
}

type Expected = {
readonly x: {
readonly a: 1
readonly b: 'hi'
}
readonly y: 'hey'
}

type Todo = DeepReadonly<X>

实现:

1
2
3
export type DeepReadonly<T> = {
readonly [P in keyof T]: keyof T[P] extends never ? T[P] : DeepReadonly<T[P]>;
};

技巧提示:

  • 通过 keyof T[P] extends never 这种方式判断是否有子属性,可以完成深度遍历

Merge 以及其他 interface 相关

原文地址

使用:

1
2
3
4
5
6
7
8
9
10
type foo = {
name: string;
age: string;
}
type coo = {
age: number;
sex: string
}

type Result = Merge<foo,coo>;

Merge 的功能和 typescript 提供的很多内置功能函数很像,这个题目实现的方式很多,我这里给出一个比较简洁的方式:

1
2
3
4
5
export type Merge<T, U> = {
[P in Exclude<keyof T, keyof U>]: T[P];
} & {
[G in keyof U]: U[G];
};

类似 Merge,我们在再给出一些其他的简单的类似 type 相关的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

export type Diff<T, U> = Omit<T & U, keyof T & keyof U>;








export type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};








export type RequiredByKeys<T, K = keyof T> = SimpleMerge<
Partial<Pick<T, Exclude<keyof T, K>>>,
Required<Pick<T, Extract<keyof T, K>>>

>;









export type Mutable<T> = {
-readonly [K in keyof T]: T[K]
}



export type GetRequired<T> = {
[P in keyof T as T[P] extends Required<T>[P] ? P : never]: T[P]
}
export type RequiredKeys<T> = keyof GetRequired<T>




export type GetOptional<T> = {
[P in keyof T as T[P] extends Required<T>[P] ? never : P]: T[P]
}
export type OptionalKeys<T> = keyof GetOptional<T>

一些技巧提示:

  • 通过组合 Typescript 内置的功能函数,我们可以完成很多复杂的业务需求。
  • 通过 -readonly 来减去修饰符。
  • 通过 [P in keyof T]-?: T[P]; 这种方式来减去可选修饰符(Typescript 内置的 Required 就是这么实现的)。
  • 通过 K in keyof T as T[K] 获取一个属性的类型,结合 extends 做条件判断。

TrimLeft 以及字符串相关

原文地址

使用:

1
type trimed = TrimLeft<'  Hello World  '> 

实际上字符串操作在 Typescript 中应该用的并不是很多,而这也是 Typescript 比较后面(4.0+)才逐渐完善的功能。
这个题目的解答:

1
2
3
4
type Space = " " | "\n" | "\t";
export type TrimLeft<S extends string> = S extends `${Space}${infer Rest}`
? TrimLeft<Rest>
: S;

实际上,我们如果知道可以这样写,还可以实现很多的类似的方式,比如可以很方便地实现 TrimTrimRight (由于相似度非常高,这两个不再罗列答案),以及 ReplaceReplaceAllDropChar 等,甚至还可以比较方便地实现类型转换,如 ParseInt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38



export type Replace<
S extends string,
From extends string,
To extends string
> = From extends ""
? S
: S extends `${infer Left}${From}${infer Right}`
? `${Left}${To}${Right}`
: S;




export type ReplaceAll<
S extends string,
From extends string,
To extends string
> = From extends ""
? S
: S extends `${infer Left}${From}${infer Right}`
? `${Left}${To}${ReplaceAll<`${Right}`, From, To>}`
: S;





export type DropChar<S, C> = S extends `${infer H}${infer R}`
? H extends C
? `${DropChar<R, C>}`
: `${H}${DropChar<R, C>}`
: "";


export type ParseInt<T extends string> = T extends `${infer Digit extends number}` ? Digit : never

另外,基于操作字符串的能力,我们还可以实现更多,比如:

  • StringToUnion 字符串转联合类型,123 -> "1" | "2" | "3"
  • KebabCase 字符串格式转换,FooBarBaz -> foo-bar-baz

等等,这些笔者没有列举具体实现,是因为认为大部分开发中用的还是不多的,如果你直接一眼就能想出方案,可能也不用去看了。

Append Argument

原文地址

使用:

1
2
3
4
type Fn = (a: number, b: string) => number

type Result = AppendArgument<Fn, boolean>

这个例子可能会在我们日常开发中用到,而且可以让我们回顾如何操作函数类型:

1
2
3
4
5
export type AppendArgument<Fn extends (...args: any) => any, A> = Fn extends (
...args: infer Args
) => infer Res
? (...arg: [...Args, A]) => Res
: never;

Append to object

原文地址

使用:

1
2
type Test = { id: '1' }
type Result = AppendToObject<Test, 'value', 4>

注意和 Merge 有所区别,这里是针对 Object 来操作

1
2
3
4
5
export type AppendToObject<T, U, V> = T extends Record<string, any>
? U extends string
? { [K in keyof T | U]: K extends U ? V : T[K] }
: T
: T;

其他

Typescript 特性相对完备,基于此可以完成非常复杂的需求,甚至使用 Typescript 来编写一个 Typescript Checker,不过笔者认为,对于时间精力有限的一般工作中的开发者来说,知道“可以这样做”,并且在适当的时候可以通过简单的资料查阅完成需求,这一点可能更重要。

经过权衡,本文中只列举了部分 type–challenges 中的内容,如果你还想了解更多,不妨看看原 github 仓库。