TS 类型体操(热身)

如今,绝大多数前端开发者现在都已经接触过 Typescript。在和同僚一起学习的过程中,我发现他们虽然在使用 Typescript,但永远止步于冒号后面加上一个 type;以至于不少人号称熟练使用 ts 几年过后,依旧处于新手村阶段。因此,我计划写一份有关类型系列的文章,来帮助大家深入理解 Typescript 的原理和一些高级操作。

type challenges

一切开始前,先介绍 type-challenges 这个开源项目——俗称类型体操。它的提供一系列从入门到放弃的练习题来训练和提升 TypeScript 类型系统的能力。这些题目涵盖了各种常见的类型操作,如条件类型、联合类型、交叉类型、映射类型等。通过完成这些题目,你可以深入理解 TypeScript 类型系统的原理,并掌握各种类型操作的使用方法。我们本系列就从type challenges开始,借助一系列的训练题,逐步掌握 ts 类型系统的各种原理和技巧。

warm up

类型体操的开始是一道热身题。如下所示:

type HelloWorld = any;
// you should make this work
type test = Expect<Equal<HelloWorld, string>>;

这里稍微提一下, ExpectEqual 是 type-challenges 提供的辅助类型,用于测试类型是否正确。你就暂且当作 jest 里的 expecttoEqual 吧。只不过这里的 ExpectEqual 是用来比较类型(抽象)的;而 jest 的 expecttoEqual 是通常用于比较值(具体)的。

这道题的考点是“起别名”,答案很简单:

type HelloWorld = string;

别名就是给一个类型起一个新名字,如上,给string类型起了一个HelloWorld的新名字。HelloWorldstring完全等价的,两个类型的引用可以互相复值,却毫无违和感。

const a: string = "hello world";
const b: HelloWorld = a;
const c: string = b;

有趣的是,我曾跟一些 Java 程序员讨论过 Typescript 中的类型别名,他们对这种概念非常陌生。或许是由于工作习惯的原因,很难理解两个不同名字的类型(或抽象)可以完全等价。同样地,我在学习 Typescript 初期,即便是多年的 js 开发,也犯过类似的错误;不过随着对类型的熟练掌握,我逐渐想明白了类型和值的关系,它们有点类似于虚数和实数的关系。在 ts 系统里, 任何定义 const val: y = x 都可以类比为复数 val = x + y*i 的形式:其中x是具体的值;而 y*i 是类型,用于约束 val 操作。在生产和工程中,虚数有点类似于辅助线,它帮助快速计算过程,但时最后的答案里虚数i却能被削掉。同理,类型别名也只是一种辅助工具,它帮助我们更好地理解类型,但在运行时,类型又不会实际产生作用。

同样地,我们既可以在实轴上能进行数据操作,也可以在虚轴上进行特定的操作。类比虚数轴上的操作,也就是我们我们可以在纯抽象的 type 上进行类型体操了。

complex.png

OK,这里可能需要点数学背景。不过,现阶段不理解也无妨。每个人都可以通过特定的抽象训练,实现对类型的深入理解,并最终吃透常规的类型系统。之后的文章中,我也会举各种 type challenges 的例子,逐步讲解抽象方面的知识点。

utility types

热身完,我们再进一步接触一下 typescript 内置的 utility types(工具类型),它们用于处理各种常见的类型问题。这些类型就像类型系统中的“函数”,能够执行各种对类型的操作。它们为开发者提供了强大的类型处理能力,使得代码更加健壮和可维护。下面我们来看最常用的一个工具类型 Record

Record<K, T>用于构造一个对象类型,其属性键为K,属性类型为T。例如:

type Person = Record<"name" | "age", string>;

const personA: Person = {
  // ✅
  name: "Tom",
  age: "18",
};

const personB: Person = {
  // ❌ Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.ts(2741)
  name: "Tom",
};

对于用过 Record 类型的朋友们,你们有没有想过Record是如何实现的呢?其实,在 Visual Studio Code 编辑器中,你可以通过按住 Control 键(或 Command 键,取决于你的操作系统)并点击Record,直接跳转到它的定义。

type Record<K extends keyof any, T> = {
  [P in K]: T;
};

这段代码表明 Record 通过类型映射将属性键映射到属性值,从而创建了一个新的对象类型。至于这里的[P in K]是什么意思呢?这也是 ts 的知识点,我们会在下一篇文章中再一一介绍,这里暂不展开了。

type mapping

了解 ts 内置的工具类,也只是到了一个入门阶段。如果要到中高阶,不得不深入理解类型映射(type mapping)。它允许我们通过映射类型“函数”来创建新的类型。举个例子,如下所示,我们可以通过某种工具类型,将一个现有的类型 A 转换成一个新的类型 B。

type A = {
  a: number;
  b: number;
};

// How to convert A to B?

type B = {
  a: string;
  b: string;
};

初学者可能没啥思路,这里直接给出答案:

type NumberToString<T> = {
  [K in keyof T]: string;
};

这里的keyof T又是什么意思呢?我再卖个官子,下一章再讲。

小结

上文中HelloWorldRecord的实现正是我们 ts 类型体操要训练的代码。这些知识主要在官方文档的types-from-types这一章节里介绍。但是这个章节的内容相对抽象,而市面上似乎也没有太多的资料介绍;因此也就有了我一开始的想法,通过一些特定的练习题,来帮助大家理解 typescript 的类型系统。

本文作为本系列的导读篇,不再深入解释更细节的知识点。在接下来的周更系列文章中,我们会碰到各式各样的训练题,然后再一一展开。希望通过这些介绍,能够帮助大家更深入地理解 TypeScript 的类型系统,从而写出更加健壮、安全的代码。敬请期待。

参考

文章同步发布于an-Onion 的 Github。码字不易,欢迎点赞。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容