理解TypeScript中“类型”的概念到底有多难?

广告位招租
扫码页面底部二维码联系

TypeScript是JavaScrip本文版权归作者所有,未经授权不得转载。【关注微信公众号:wwwtangshuangnet】t的超集。这是最大的一个误解!TS并不能【版权所有,侵权必究】【转载请注明来源】在没有任何的条件下,包含JS,你必须升级【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.netTS编译器来支持新的JS特性,所以,TS【本文受版权保护】未经授权,禁止复制转载。并不是JS的超集,而是以JS为编译目标的【本文首发于唐霜的博客】本文作者:唐霜,转载请注明出处。另一门语言。TypeScript的核心概【原创内容,转载请注明出处】著作权归作者所有,禁止商业用途转载。念就是“类型”,对于很多初接触TS的同学【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.net,类型就是冒号后面的内容,然而,事实真的原创内容,盗版必究。【本文受版权保护】是这样吗?本文将从一个另类的角度,聊一聊【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。TS里面的泛型、&、子类型、类型【作者:唐霜】未经授权,禁止复制转载。推导、类型空间等话题,从而为你展现一个可【本文受版权保护】【作者:唐霜】能从来没想过的TS类型概念。

【版权所有,侵权必究】【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。【本文受版权保护】

类型声明文件.d.ts转载请注明出处:www.tangshuang.net

本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net【转载请注明来源】

一切先从.d.ts文件开始说起吧。在我们【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。已经写好的js库中,可以通过.d.ts向本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。外提供本库的类型声明,以方便类似vsco【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。de之类的编辑器可以智能提示和补全,以及【作者:唐霜】【关注微信公众号:wwwtangshuangnet】在ts项目中正确推导本库的api用法。在【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。.d.ts文件中,我们通过declare转载请注明出处:www.tangshuang.net原创内容,盗版必究。来对需要暴露的api进行声明。

【本文受版权保护】【作者:唐霜】转载请注明出处:www.tangshuang.net【作者:唐霜】

declare是一个新的关键字,起码我们在以前只写js转载请注明出处:www.tangshuang.net原创内容,盗版必究。的生涯中,从来没有使用过。我们如下声明一【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。个函数:

著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】【作者:唐霜】未经授权,禁止复制转载。
declare function plus(a: number, b: number): number;

在声明中,我们只提供了函数的类型描述(下【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】文我们会用形状Shape来表达这一概念)【本文首发于唐霜的博客】【原创不易,请尊重版权】,而没有函数的具体内容。这就像接口(in本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。terface)一样,只有描述,没有实现著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。。如果我们的库是以模块的形式提供给外部使【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。用,那么,只需要在最前面加上export著作权归作者所有,禁止商业用途转载。【本文受版权保护】导出这个函数的描述:

本文作者:唐霜,转载请注明出处。【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。
export declare function plus(a: number, b: number): number;

这样,在这个库的外部,当我们通过impo【访问 www.tangshuang.net 获取更多精彩内容】【本文受版权保护】rt导入它时,ts就会把它当作一个ES模【访问 www.tangshuang.net 获取更多精彩内容】【转载请注明来源】块,并从模块中提供暴露的plus接口给外本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。部的这个项目使用。

本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。

在.d.ts文件中,我们不会存在任何js本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net的具体实现。?真的吗?也不一定,有时候我【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。们会独立声明一个enum,而此时,你需要原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net给定具体的值,以方便在外部阅读。

【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。
export enum Colors {
  Red: 'red';
  Blue: 'blue';
  Yellow: 'yellow';
}

export declare function paint(color: Colors): void;

注意第一个export,倘若此处我们不导本文作者:唐霜,转载请注明出处。【未经授权禁止转载】出enum,那么我们在外部只能通过硬编码【版权所有,侵权必究】【版权所有】唐霜 www.tangshuang.net的形式传入字符串的red/blue/ye【未经授权禁止转载】【未经授权禁止转载】llow,但当我们导出之后,就可以在外部著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。使用Colors.Red等:

【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】
import { Colors } from 'some-lib/index.d' // <- 把它当作一个普通的.ts导入
import { paint } from 'some-lib'

paint(Colors.Red)

.d.ts只是一个摘要文件,它不被作为真【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】正的运行时代码进行编译,但同时,我们也可【原创不易,请尊重版权】著作权归作者所有,禁止商业用途转载。以把它作为一个.ts文件进行使用,使用时【作者:唐霜】未经授权,禁止复制转载。,通过declare声明的部分,不会被作为运行时进行编译。【本文首发于唐霜的博客】

原创内容,盗版必究。本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net

当你读到这里的时候,我想,你应该对dec【本文首发于唐霜的博客】【本文首发于唐霜的博客】lare产生了兴趣,以及对带来这个新语法【作者:唐霜】著作权归作者所有,禁止商业用途转载。的新语言产生了兴趣。但是,不要着急,我们【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.net还有很多东西要聊。

【原创内容,转载请注明出处】【原创内容,转载请注明出处】【原创内容,转载请注明出处】

&不是“交集”?著作权归作者所有,禁止商业用途转载。

原创内容,盗版必究。【本文受版权保护】【本文首发于唐霜的博客】

我们直观感受是,当看到&时,我们转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net要得到一个交集。例如:

【原创内容,转载请注明出处】【未经授权禁止转载】【原创内容,转载请注明出处】未经授权,禁止复制转载。【本文受版权保护】
interface A {
  name: string;
  age: number;
}

interface B {
  name: string;
  weight: number;
}

我们直观感受是,A&B ->【原创不易,请尊重版权】【本文受版权保护】; interface C { name【原创内容,转载请注明出处】原创内容,盗版必究。: string },然而,事实恰恰相反【访问 www.tangshuang.net 获取更多精彩内容】【本文受版权保护】,A&B的结果是:

【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。
interface D {
  name: string;
  age: number;
  weight: number;
}

咦……这……不是并集吗?为了便于理解,你【本文首发于唐霜的博客】本文作者:唐霜,转载请注明出处。可以这样想:D既符合A也符合B,所以是A【本文受版权保护】【原创不易,请尊重版权】和B的“交叉”。但是,如果只能通过这种方【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】式来记忆TS,那么,真的毫无意义,掌握不【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。到TS的皮毛。

未经授权,禁止复制转载。【关注微信公众号:wwwtangshuangnet】【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。

此extends非彼extends原创内容,盗版必究。

原创内容,盗版必究。原创内容,盗版必究。原创内容,盗版必究。

TS中的extends和JS里面的ext【作者:唐霜】【未经授权禁止转载】ends形式相同概念不同。例如:

转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。
interface Animal {
  name: string;
}

interface Snake extends Animal {
  length: number;
}

Snake是在Animal的基础上增加了未经授权,禁止复制转载。【本文首发于唐霜的博客】length字段。我们可以换一种写法:

【作者:唐霜】【访问 www.tangshuang.net 获取更多精彩内容】【原创不易,请尊重版权】
type Snake = Animal & {
  length: number,
}

两者在语义上是有差别的,虽然结果上一致。【作者:唐霜】【原创不易,请尊重版权】太不可思议了,我看到了感觉上完全不同的两【转载请注明来源】【本文首发于唐霜的博客】种操作,竟然得到了相同的结果?

转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。

extends在TS中,代表着从一个类型扩展出另外一个【访问 www.tangshuang.net 获取更多精彩内容】【本文受版权保护】新类型,这个新类型是原来这个类型的子类型转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】。同时,extends在TS中具有条件属【本文受版权保护】【版权所有】唐霜 www.tangshuang.net性,它用于判断一个类型是否是另外一个类型著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】的子类型:

【转载请注明来源】【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。未经授权,禁止复制转载。未经授权,禁止复制转载。
type Some = A extends B ? C : D

在这一意义上,TS和JS中的extend本文作者:唐霜,转载请注明出处。【原创内容,转载请注明出处】s具有完全不同的性质。JS中extend转载请注明出处:www.tangshuang.net【版权所有,侵权必究】s代表继承(inherit),而TS中e未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。xtends就是它的字面意思“扩展”。

未经授权,禁止复制转载。未经授权,禁止复制转载。【本文受版权保护】【转载请注明来源】【本文首发于唐霜的博客】

Subtyping子类型本文作者:唐霜,转载请注明出处。

著作权归作者所有,禁止商业用途转载。【作者:唐霜】著作权归作者所有,禁止商业用途转载。

子类型是一个庞大的话题,涉及到抽象概念、【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。(推导)规则、协变、逆变等等,你可以阅读这篇文章深入了解【转载请注明来源】,本文只会聊其中很小的一部分。子类型揭示【未经授权禁止转载】未经授权,禁止复制转载。了TS类型系统的核心实质,它是一个推导系【未经授权禁止转载】【原创内容,转载请注明出处】统,推导即基于某些定理、公理、定律进行演未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。算的过程(在TS中主要是基于内建的一些规著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。则用于检查值的类型)。

转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。

TS的类型基于“集合”的概念。一个类型,【版权所有】唐霜 www.tangshuang.net【作者:唐霜】代表一个集合,例如string类型代表的本文作者:唐霜,转载请注明出处。【未经授权禁止转载】是所有字符串的集合。在检查过程中,如何决【访问 www.tangshuang.net 获取更多精彩内容】【转载请注明来源】定一个值属于这个“集合”呢?它的依据在于本文作者:唐霜,转载请注明出处。【本文首发于唐霜的博客】这个值的“形状(Shape)”。怎么定义转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net值的形状呢?它由两部分组成,一部分是基于著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】JS的基础类型,得到该值的数据类型,另一【关注微信公众号:wwwtangshuangnet】转载请注明出处:www.tangshuang.net部分是基于TS的另外一个核心Struct【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。ural(结构化的),得到该值的结构。基著作权归作者所有,禁止商业用途转载。【本文受版权保护】于数据类型和结构这两个概念,TS获得值的【作者:唐霜】【版权所有】唐霜 www.tangshuang.net形状,基于“推导”的运行方式,决定该值是本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net否符合某个类型。

原创内容,盗版必究。原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】【未经授权禁止转载】

正是这样,那么下面两个类型,在TS里面,【转载请注明来源】未经授权,禁止复制转载。竟然是“相等”的:

【转载请注明来源】未经授权,禁止复制转载。未经授权,禁止复制转载。
class A {
  name: string;
}

class B {
  name: string;
}

这在Java或C#这样的语言中,完全不能【转载请注明来源】原创内容,盗版必究。被理解,它俩怎么可能相等?而在TS中,它原创内容,盗版必究。【本文受版权保护】们代表着形状为 { name: stri本文版权归作者所有,未经授权不得转载。【转载请注明来源】ng } 的对象(JS中一切复合类型皆是原创内容,盗版必究。原创内容,盗版必究。对象)的集合。一个值,在TS中,它和集合【转载请注明来源】原创内容,盗版必究。的对应关系不是一对一的,它可以同时属于多著作权归作者所有,禁止商业用途转载。【转载请注明来源】个集合中,是一对多的关系。而同时,两个不【作者:唐霜】未经授权,禁止复制转载。同的集合,却可能表达相同的形状。如果你看未经授权,禁止复制转载。【版权所有,侵权必究】它像鸭子,那么它就是鸭子。于是,A和B相未经授权,禁止复制转载。【版权所有,侵权必究】等了,只是取了不同的名字而已,就像一个人【本文受版权保护】【版权所有,侵权必究】在这个杂志上叫鲁迅,在另一个杂志上叫润土【未经授权禁止转载】【版权所有,侵权必究】

本文版权归作者所有,未经授权不得转载。【本文受版权保护】本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】

TS是怎么对待(treat)这些集合的呢著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】?它基于一种推导的范式确定两个集合之间的转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net关系。两个集合存在什么关系呢?只存在两种【未经授权禁止转载】【转载请注明来源】关系:一种是A是B的子类型,另一种是A不本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。是B的子类型。(A和B可交换位置。)子类【版权所有】唐霜 www.tangshuang.net【未经授权禁止转载】型,是TS对待类型集合的唯一方式。在TS【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】中,所有类型的总和,都处在子类型体系中,【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net这个子类型体系是一个树状网络,它的根是一【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net个叫unknow的类型,而它的底是一个叫【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。never的类型(never是所有类型的【未经授权禁止转载】原创内容,盗版必究。子类型,是树状的所有叶子)。

转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。

子类型用于表达类型与类型的【本文首发于唐霜的博客】二元关系原创内容,盗版必究。,当一个值的类型属于某一类型时,它同时属【作者:唐霜】未经授权,禁止复制转载。于该类型的父类型。它的产生方式又有多种,【本文受版权保护】著作权归作者所有,禁止商业用途转载。一种是基于父类型扩展,也就是使用exte【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。nds,一种是基于一种叫交叉(A &am本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。p; B)的操作得到,一种则是基于一种叫转载请注明出处:www.tangshuang.net【版权所有,侵权必究】联合(A | B)的操作得到。曾经有这样本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。一个预言,让一只猴子坐在一台电脑前敲击键【本文首发于唐霜的博客】【转载请注明来源】盘,总有一天它能敲出莎士比亚的所有著作(未经授权,禁止复制转载。【原创不易,请尊重版权】猴子不懂艺术)。同样的道理,只要给定条件【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。,你永远可以在TS的子类型体系中找到对应【访问 www.tangshuang.net 获取更多精彩内容】【本文受版权保护】的类型,而这个过程基于“推导”完成。因此【原创内容,转载请注明出处】【转载请注明来源】,实际上,子类型的产生方式只有一种而非三【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】种或更多,这种方式就是基于某一类型(ne【未经授权禁止转载】【未经授权禁止转载】ver除外)扩展出新类型。

本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】【版权所有】唐霜 www.tangshuang.net

字面量类型本文作者:唐霜,转载请注明出处。

【访问 www.tangshuang.net 获取更多精彩内容】【版权所有,侵权必究】转载请注明出处:www.tangshuang.net

基于“形状”我们确实可以做类型的断言,但【本文受版权保护】【作者:唐霜】是这套方法会有个例外:字面量类型。特别是原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。基础类型的字面量类型,比如string,【关注微信公众号:wwwtangshuangnet】【本文受版权保护】 number等的字面量。字面量类型只有转载请注明出处:www.tangshuang.net【转载请注明来源】结构(structure)没有数据类型,原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。这听上去有点反常识,且往下读。

本文作者:唐霜,转载请注明出处。【本文首发于唐霜的博客】【原创不易,请尊重版权】【原创内容,转载请注明出处】
type Str = 'hello' | 'world'
let a: Str = 'hello'

我们在用字面量类型时,常常并不是按照我们原创内容,盗版必究。【转载请注明来源】所想的那样工作,例如:

原创内容,盗版必究。转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。
type Source = {
  name: string,
  ext: 'mp3' | 'mp4',
}
function play(source: Source): void {
  ...
}
const source = { 
  name: 'xxx',
  ext: 'mp3',
}
play(source) // <- error

此时却会抛出错误。这是因为当TS在断言时转载请注明出处:www.tangshuang.net【作者:唐霜】,是以形参source的形状进行推导,对未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net实参source的形状进行断言。因此,接【未经授权禁止转载】【原创不易,请尊重版权】收到的参数在形状上是 { name: s本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】tring, ext: string }【版权所有】唐霜 www.tangshuang.net本文作者:唐霜,转载请注明出处。 并不是Source的子类型,所以TS认【本文受版权保护】【原创内容,转载请注明出处】为此处检查不通过。解决办法是在实参sou著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。rce.ext后面跟上as const,本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。告诉编译器此处我强制’mp3未经授权,禁止复制转载。未经授权,禁止复制转载。’为常量,因此就符合Sour【本文受版权保护】【未经授权禁止转载】ce的约束。

【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。

但这个问题不是由字面量本身引起的,我们看未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。另外一个case:

本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】
type Ext = 'mp3' | 'mp4'

const mp3 = 'mp3'

function play(ext: Ext): void {}

play(mp3) // <- ok

字面量本身并不会带来问题,问题在于mut未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。(可变)。假如我上面把const mp3原创内容,盗版必究。【未经授权禁止转载】改为let mp3,此时就会报错。在pl原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。ay接收source的时候,虽然此时so【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。urce.ext的值为mp3,但是,并不【原创内容,转载请注明出处】【原创不易,请尊重版权】代表source.ext的值永远为mp3【未经授权禁止转载】【转载请注明来源】,因为它是可变的。假如它是不可变的,那么【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】就没有问题。

【原创不易,请尊重版权】著作权归作者所有,禁止商业用途转载。【转载请注明来源】未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。

问题解决了,现在,让我们回到类型系统的讨【本文首发于唐霜的博客】转载请注明出处:www.tangshuang.net论上来。

【原创不易,请尊重版权】【未经授权禁止转载】【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】

上文提到TS基于推导进行类型断言,推导就【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net是寻找子类型二元关系,如果不存在父子类型【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】关系,就断言失败,抛出错误。那么,【原创不易,请尊重版权】【原创内容,转载请注明出处】217;mp3′ | 【原创不易,请尊重版权】原创内容,盗版必究。216;mp4′ 的子类型是原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net谁呢?’mp3’【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。, ‘mp4′ 未经授权,禁止复制转载。原创内容,盗版必究。和 never,以及它自身。

本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】【版权所有,侵权必究】【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】

现在,’mp3’【作者:唐霜】【原创内容,转载请注明出处】 & ‘mp4原创内容,盗版必究。【未经授权禁止转载】217;的子类型时谁呢?就是它的本身ne【版权所有,侵权必究】【作者:唐霜】ver。为什么它的本身是never呢?我【原创内容,转载请注明出处】【转载请注明来源】们来看,’mp3’著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。;的子类型是什么?’mp3&【原创内容,转载请注明出处】【本文受版权保护】#8217;和never。’【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。mp4’的子类型呢?R转载请注明出处:www.tangshuang.net【本文受版权保护】17;mp4’和never。【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。只有never满足’mp3&本文版权归作者所有,未经授权不得转载。【转载请注明来源】#8217;&’mp原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】4’。

【作者:唐霜】未经授权,禁止复制转载。原创内容,盗版必究。【转载请注明来源】【本文首发于唐霜的博客】

我们回过头来看文章前面提到的A&本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】B的例子。

【转载请注明来源】【版权所有,侵权必究】【未经授权禁止转载】
interface A {
  name: string;
} // -> { name: string }

interface A1 extends A {
  age: number;
} // -> { name: string, age: number }

interface A2 extends A {
  weight: number;
} // -> { name: string, weight: number }

interface B {
  sex: string;
} // -> ???

谁是A的子类型一目了然了吧。所有含有na转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】me:string的形状都是A的子类型,【原创不易,请尊重版权】著作权归作者所有,禁止商业用途转载。包括A本身,以及never。那么,谁是B【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。的子类型呢?所有包含sex:string【访问 www.tangshuang.net 获取更多精彩内容】【未经授权禁止转载】的形状都是B的子类型,包括B本身,以及n【版权所有】唐霜 www.tangshuang.net【未经授权禁止转载】ever。

本文版权归作者所有,未经授权不得转载。【作者:唐霜】本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】【未经授权禁止转载】

现在,我们知道了A&B本身是A&转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】amp;B的子类型,那么A&B是原创内容,盗版必究。【关注微信公众号:wwwtangshuangnet】什么?是指“既是A的子类型,也是B的子类【关注微信公众号:wwwtangshuangnet】【本文受版权保护】型”的类型,也就是A的子类型的集合与B的【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。子类型的集合的交集。所以,A&B【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.net表达的是这样的“交集”,而实际产生的结果【未经授权禁止转载】原创内容,盗版必究。是形状的“并集”,这并不是矛盾的,而是推【版权所有】唐霜 www.tangshuang.net【未经授权禁止转载】导结果。

【原创不易,请尊重版权】未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。【未经授权禁止转载】【本文首发于唐霜的博客】

纯类型编程转载请注明出处:www.tangshuang.net

【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.net【转载请注明来源】本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。

TS的类型系统几乎快要成为图灵完备的一门【未经授权禁止转载】【原创内容,转载请注明出处】语言,你可以用它来写出一门新语言【转载请注明来源】。我们要理解的是,纯类型编程和作为JS超转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net集的TS编程的边界。我们几乎不会只写类型本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。,而不写JS代码……等一下,真的不会吗?原创内容,盗版必究。【本文受版权保护】我们有的时候,会把项目中反复用到的一些类本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】型,提取到公用的typings目录下进行转载请注明出处:www.tangshuang.net【本文受版权保护】管理,在其他地方引入这些类型。当我们脱离【访问 www.tangshuang.net 获取更多精彩内容】本文作者:唐霜,转载请注明出处。JS单纯写类型的时候,我们开始进入另外一【原创内容,转载请注明出处】本文版权归作者所有,未经授权不得转载。个世界,这个世界叫类型空间。

原创内容,盗版必究。未经授权,禁止复制转载。【未经授权禁止转载】本文作者:唐霜,转载请注明出处。

我们写JS处于一个叫值空间的世界,面向运【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。行时编程。而在类型空间,我们面向编译时编【作者:唐霜】著作权归作者所有,禁止商业用途转载。程。大多数情况下,两个空间是隔绝的(虽然在写原创内容,盗版必究。本文作者:唐霜,转载请注明出处。代码的时候大部分情况下它们被写在一起),【版权所有】唐霜 www.tangshuang.net【转载请注明来源】但偶尔也有例外,也就是字面量类型出现的时本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。候,TS会在两个世界传送字面量(的结构而【原创不易,请尊重版权】【版权所有】唐霜 www.tangshuang.net非值),以完成类型识别。所以严格讲,值,原创内容,盗版必究。未经授权,禁止复制转载。永远不会出现在类型空间。

【原创内容,转载请注明出处】【原创不易,请尊重版权】【本文受版权保护】未经授权,禁止复制转载。

在官方网站有这样一句话:别名只是别名。它【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。的意思是,你并非在用type关键字定义一【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】个类型,你只是给一个类型取了一个别名。无【版权所有,侵权必究】【未经授权禁止转载】论你给一个类型取了多少个别名,它还是它自原创内容,盗版必究。【原创内容,转载请注明出处】己,没有发生任何变化。

【访问 www.tangshuang.net 获取更多精彩内容】【转载请注明来源】转载请注明出处:www.tangshuang.net

联合类型或交叉类型不具备存储类型的功能。【版权所有,侵权必究】本文版权归作者所有,未经授权不得转载。例如type C = A & B【作者:唐霜】原创内容,盗版必究。,C并不能存储一个具体的类型,而是存储一原创内容,盗版必究。转载请注明出处:www.tangshuang.net个推导逻辑。原因很简单,因为C是无数个类【本文首发于唐霜的博客】【转载请注明来源】型的集合。而在前面我们讲过,一个类型是一【原创内容,转载请注明出处】【未经授权禁止转载】个集合,所以,C是无数个集合的集合。你不转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net可能存储无数个类型。C参与断言的方式,是本文作者:唐霜,转载请注明出处。【版权所有,侵权必究】将具体的形状,参与推导过程。举个例子:有转载请注明出处:www.tangshuang.net【未经授权禁止转载】一条这样的规则:S-Arrow: 对于任著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】何类型T1【版权所有】唐霜 www.tangshuang.net,T转载请注明出处:www.tangshuang.net2本文作者:唐霜,转载请注明出处。,S【原创不易,请尊重版权】1【关注微信公众号:wwwtangshuangnet】,S未经授权,禁止复制转载。2【版权所有】唐霜 www.tangshuang.net ,如果T著作权归作者所有,禁止商业用途转载。1【版权所有】唐霜 www.tangshuang.net<:S本文版权归作者所有,未经授权不得转载。1著作权归作者所有,禁止商业用途转载。, 而且S【作者:唐霜】2本文版权归作者所有,未经授权不得转载。<:T转载请注明出处:www.tangshuang.net2原创内容,盗版必究。,那么S【未经授权禁止转载】1【转载请注明来源】->S转载请注明出处:www.tangshuang.net2【原创不易,请尊重版权】 <: T【版权所有】唐霜 www.tangshuang.net1未经授权,禁止复制转载。->T本文作者:唐霜,转载请注明出处。2著作权归作者所有,禁止商业用途转载。【访问 www.tangshuang.net 获取更多精彩内容】

【版权所有】唐霜 www.tangshuang.net【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】
declare const foo: (x: unknown) => string; // S1->S2
const bar: (x: number) => unknown = foo; // T1->T2

在上面的这个例子中,TS可以根据S-Ar【原创不易,请尊重版权】【原创不易,请尊重版权】row规则,快速断言foo的类型是bar【本文首发于唐霜的博客】转载请注明出处:www.tangshuang.net的类型的子类型,所以,将foo赋值给ba【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.netr是完全ok的操作。类似这样的规则,是具未经授权,禁止复制转载。【作者:唐霜】有数学意义的公理、定理或定律,有的是前提本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。条件,有的是推导结论,在TS的断言中,都【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net可以直接使用。基于这些推导规则,TS并不著作权归作者所有,禁止商业用途转载。【本文受版权保护】需要确定一个别名的具体类型,而可以做到编著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net译时实时且高效的推导和断言。

本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net【转载请注明来源】原创内容,盗版必究。

“类型”是TS世界的一等公民,是唯一的主【原创内容,转载请注明出处】著作权归作者所有,禁止商业用途转载。角,TS类型编程无非是基于一个或多个类型未经授权,禁止复制转载。【作者:唐霜】,生成其他类型。但类型世界的运转规则和值原创内容,盗版必究。【本文受版权保护】世界的运转规则完全不同。值世界的新值由计未经授权,禁止复制转载。【作者:唐霜】算得到,计算规则有常见的运算、辅助运算规【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。则、内存与数据结构、位运算等等。类型世界本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。的新类型如上文所述,由父类型扩展而来,扩著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。展规则有扩展、联合、交叉、推导、获取等。转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net类型具有抽象性,TS没有命令指令,因此没【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。有副作用,因此类型只能被创建无法被修改,转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。因此TS是静态的系统。没有副作用并不代表【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。无法实现编程,和值世界完全不同的运行规则【原创内容,转载请注明出处】【版权所有,侵权必究】,让TS可以基于推导完成复杂的编程,而我【本文受版权保护】【原创内容,转载请注明出处】们常把这种需要动脑子找到推导路径的过程称转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net为“类型体操”,例如,我们找出从A|B推著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】导出A&B的路径是type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
【访问 www.tangshuang.net 获取更多精彩内容】

【本文受版权保护】转载请注明出处:www.tangshuang.net【版权所有,侵权必究】

除了借用JS的typeof, in等指令转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。关键字外,TS的keyof, infer【版权所有,侵权必究】转载请注明出处:www.tangshuang.net, as等指令,都不具备副作用。同时,在【作者:唐霜】【本文首发于唐霜的博客】TS中,也支持三目运算,根据条件选择使用原创内容,盗版必究。【作者:唐霜】类型,例如 T extends Some本文作者:唐霜,转载请注明出处。【本文受版权保护】 ? T : Some. TS直接支持递未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net归,可配合泛型用于替代循环语句。keyo本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.netf和in配合可用于支持迭代遍历效果。泛型【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】,则是通往类型编程的高速公路,是实现类型著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.net编程的核心条件。

未经授权,禁止复制转载。【本文首发于唐霜的博客】【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。

我在未经授权,禁止复制转载。之前的一篇博客文章中【原创内容,转载请注明出处】有聊过自己第一次接触泛型时,如何用已知的未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net知识理解它。但那种理解仍然是套用知识,而【作者:唐霜】【作者:唐霜】非认知。简单讲,泛型是TS类型编程中的“【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】函数”,用以根据已有类型,按照给定推导路【本文受版权保护】【未经授权禁止转载】径,生成新的类型,可以简称为“类型生成函转载请注明出处:www.tangshuang.net【版权所有,侵权必究】数”。泛型参数是TS中最有趣最灵活最强大著作权归作者所有,禁止商业用途转载。【访问 www.tangshuang.net 获取更多精彩内容】最麻烦的存在。我们来看一下一个解构的例子本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。

著作权归作者所有,禁止商业用途转载。【未经授权禁止转载】原创内容,盗版必究。【未经授权禁止转载】【本文首发于唐霜的博客】
const { name } = { name: 'yj' } // javascript

我们尝试用TS类型编程来做类似的事情(【本文首发于唐霜的博客】出自【版权所有】唐霜 www.tangshuang.net):【原创不易,请尊重版权】

转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.net原创内容,盗版必究。
type NameType<T> = T extends { name: infer N } ? N : never;
type name = NameType<{ name: 'yj' }>; // name is 'yj'

NameType可以从给定的类型中解构出本文版权归作者所有,未经授权不得转载。【作者:唐霜】给定类型的name属性的类型’【关注微信公众号:wwwtangshuangnet】【原创不易,请尊重版权】;yj’。前文提到在类型空间本文作者:唐霜,转载请注明出处。原创内容,盗版必究。没有值,但是此刻,我们却可以利用TS的类【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。型编程能力,获得的是泛型参数类型的nam【本文首发于唐霜的博客】【版权所有,侵权必究】e属性类型(字面量)。

本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。

但是遗憾的是,TS没有输出指令,类型世界【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】的结果也无法传递给值世界,所以,我们在类【作者:唐霜】【原创内容,转载请注明出处】型世界的编程,最终无法产生效果。不过随着【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。时间的推移,事情有了转机,TS官方提供了【作者:唐霜】原创内容,盗版必究。生成器接口,通过一些工厂方法,你可以修改【本文受版权保护】转载请注明出处:www.tangshuang.netTS的编译结果。而在这个过程中,我们可以原创内容,盗版必究。【作者:唐霜】尝试破坏TS的Erased特性,保留类型【转载请注明来源】【转载请注明来源】的某些信息,从而可以将类型世界的结果传送转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。回值的世界,输出类型编程的结果。

【作者:唐霜】转载请注明出处:www.tangshuang.net【本文受版权保护】【版权所有,侵权必究】

结语未经授权,禁止复制转载。

【本文首发于唐霜的博客】【作者:唐霜】著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】

本文并没有展开typescript中关于【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net类型的用法,本文从另外一个角度,探索ty转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。pescript中“类型”的概念,其中很原创内容,盗版必究。【作者:唐霜】多表述可能并不准确甚至并不正确,但是,我【原创不易,请尊重版权】【关注微信公众号:wwwtangshuangnet】努力抛开用法,从本源出发去思考types【本文首发于唐霜的博客】本文作者:唐霜,转载请注明出处。cript中最核心(甚至是唯一)的概念。未经授权,禁止复制转载。【访问 www.tangshuang.net 获取更多精彩内容】我们在写TS代码的时候,或许也该意识到,本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】我们在做一件在两个世界之间穿越的事情,听【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net起来很魔幻。掌握TS中类型的规律,我们必转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】须深入去思考子类型是什么,并且在子类型体【版权所有,侵权必究】转载请注明出处:www.tangshuang.net系中,寻求我们的最短路径。

本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】【关注微信公众号:wwwtangshuangnet】【作者:唐霜】

2021-06-27 8308

为价值买单,打赏一杯咖啡

本文价值83.08RMB
已有5条评论
  1. money 2021-07-17 21:31

    技术文章,学习了。

    文章真长

  2. 1188 2021-06-28 20:17

    很有价值的一文, 解释了为什么我一会儿觉得ts好写, 一会儿就觉得ts好难写.
    还有个小疑惑, as const解决完字面量的问题之后数组的类型又会报错, 这时候是怎么解决的?

    “`
    type Source = {
      name: string,
      ext: ‘mp3’ | ‘mp4’,
      list:{name:string}[]
    }
    function play(source: Source): void {
    }
    const source = {
      name: ‘xxx’,
      ext: ‘mp3’,
      list:[]
    } as const
    play(source) // <- error
    /**
    * Argument of type '{ readonly name: "xxx"; readonly ext: "mp3"; readonly list: readonly []; }' is not assignable to parameter of type 'Source'.
      Types of property 'list' are incompatible.
        The type 'readonly []' is 'readonly' and cannot be assigned to the mutable type '{ name: string; }[]'.(2345)
    */
    “`

    • 否子戈 2021-07-01 09:53

      const source = {
        name: ‘xxx’,
        ext: ‘mp3’ as const,
        list:[]
      }
      play(source)

      • 1188 2021-07-09 17:22

        感谢解惑, 回头看看, 其实文中已经给出了答案, 还是我对mut的理解不到位.

  3. rxliuli 2021-06-28 03:40

    类型体操可真是太秀了( ̄~ ̄)