我为什么会这样念念又不忘 / 你用什么牌的箭刺穿我心脏

我也久经沙场 / 戎马生涯 / 依然 / 被一箭刺伤

——李荣浩《念念又不忘》

接下来我会分上、下两篇文章介绍 TypeScript。

我也是 TypeScript 初学者,这两篇文章是我的学习笔记,来源于一个系列的免费视频,视频不错,如果你觉得看视频学习更快,可以点击这里看到。

TypeScript 与 JavaScript 有何区别呢?简单点说,就是前者引入了类型约束,能实现代码的静态检查,而且能提供更加完善的代码提示功能;除此之外,还引入了接口、抽象类、枚举、访问控制修饰符、泛型等语法,让我们写出的代码更具健壮性和可扩展性。

上、下两篇的文章内容安排如下:

  • 《上篇》

    1. 基本类型
    2. 对象类型
    3. 扩展类型
  • 《下篇》
    1. 面向对象编程
    2. 泛型

《上篇》定义为基础篇,《下篇》定义为深入篇。

我们先从基础篇开始学习。

开发环境

TypeScript 脚本以 .ts 后缀结尾。我使用 VSCode 学习 TypeScript,写好 TypeScript 代码后, 需要将其编译为 JavaScript 代码才能运行。在此之前,我们先要安装 Node.js 环境。

然后安装 TypeScript 编译环境。

$ npm install -g typescript
$ tsc --version
复制代码

编译文件的指令如下:

// 执行完下列语句后,会在同级目录下看到一个 `HelloWorld.js` 文件,
// 就是编译完成后的文件啦
$ tsc HelloWorld.ts
复制代码

codepen.io 中也支持 TypeScript 的书写。新建一个新的 Pen 后,将 JavaScript 一栏的预处理器设置成 TypeScript 即可,你也可以实时查看到编译之后的代码,不过代码提示效果不是很强。

基本类型

我们先从最简单的基本数据类型(Primitive)说起。

ECMAScript 提供了六种基本数据类型:布尔、数值、字符串、Null、Undefined 和 Symbol。

而 TypeScript 针对上述的每一种类型,都提供了对应的类型字面值:boolean、number、string、null、undefiend 和 symbol(ES6 中引入,本系列两篇不对 symbol 做介绍)。

在 TypeScript 中,使用 : type 语法为变量指定类型:

// 声明一个布尔值类型变量 `isMale`,初始值为 `true`
let isMale: boolean = true;
// 声明一个字符串类型变量 `myName`,初始值为 `'Alex'`
let myName: string = 'Alex';
// 声明一个数值类型变量 `myAge`,初始值为 `20`
let myAge: number = 20;
// 声明一个 Null 类型变量 `myGirlFriend`,值为 `null`
let myGirlFriend: null = null;
// 声明一个 Undefined 类型变量 `myHouse`,值为 `undefined`
let myHouse: undefined = undefined;
复制代码

注意:TypeScript 提供的类型字面值都是小写形式,注意与首字母大写的形式区分,后者是 JavaScript 原生提供的构造器函数。

有时,一个变量的类型并不局限于一种。比如,一个变量的值可以是字符串,也可以是数值。这时就要用到“联合类型”了。

联合类型使用竖线 | 分隔,表示某个变量可以给予其中任意一种类型值。

下例中,声明了一个变量 foo,它的值可以是一个字符串,也可以是一个数值。

// 此处声明了一个变量 `foo`,可以是字符串,也可以是数值
let foo: string | number = 'bar'; // 初始值给了字符串 `'bar'`
foo = 123; // 接下来将 `foo` 重新赋值为 `123`
复制代码

对象类型

除了基本类型,工作中最常处理就是对象了。那么如何在 TypeScript 中指定对象类型呢?

定义对象

在 TypeScript 中,使用接口,也就是关键字 interface 来描述对象的形状,也就是对象的类型。“接口”在传统的面向对象编程的语言里,比如 Java,表示“行为的抽象”,而在 TypeScript 对此稍有不同,接口不仅可以表示行为的抽象,还可以用来定义对象类型。

接下来,我们定义一个类型 Person(按照约定,首字母大写):

// 用接口声明一个类型 `Person`
interface Person {name: string;age: number;
}// 将变量 `alex` 声明为 `Person` 类型
let alex: Person = {name: 'Alex',age: 20
};
复制代码

注意:定义接口时,属性之间可以用分号 ;、也可以用逗号 , 分隔,甚至什么都不加也可以。

我们定义了一个类型 Person,并将变量 alex 的类型声明为 Person。那么,在给 alex 赋值时,必须严格符合类型定义:赋值对象必须由一个字符串属性 name和一个数值属性 age 组成,缺少或多出的属性,都会提示错误。

// 会提示出错(缺少一个属性)
let alex: Person = {name: 'Alex';
};// 会提示错误(多了一个属性)
let alex: Person = {name: 'Alex',age: 20,gender: 'male'
};
复制代码

定义类型时,如果想要表示某个属性是可选的,则使用 ?: type 语法声明。

// 类型 `Person` 的 `name` 属性是可选的
interface Person {name: string;age?: number;
}// 因为 `age` 是可选属性,所以赋值时不给也行
let alex: Person = {name: 'Alex'
};
复制代码

除了定义可选属性,还可以定义“任意属性”。

所谓的任意属性,就是我们不确定将来会添加的属性名称是什么,但是会提前定义允许添加的属性,在不确定未来这个属性名的情况下,限制这个属性的类型。

// Person 中定义了一个任意属性,属性类型是 `any`
interface Person {name: string;age?: number;[propName: string]: any;
}// 我们给变量 `alex` 添加了一个任意属性 `gender`
let alex: Person = {name: 'Alex',gender: 'male
};
复制代码

我们使用 [propName: string]: any 的形式,定义了一个 any 类型的任意属性。

注意,任意属性的类型,必须是上面的已知属性 nameage 类型的超集,否则会提示出错。 比如,上面我们可以将上面任意属性的类型 any 修改为 string | number 也是可以的。

说完对象,再来介绍数组。

数组类型

在数组上指定类型,本质上是限制数组成员的类型。在 TypeScript 中,使用 type[] 语法指定数组成员的类型。

下面定义了一个数组,限制其成员只能是字符串。

// 此处定义了一个数组 `myFriends`,其成员限定为只能是字符串
let myFriends: string[] = ['Alex', 'Bob'];
复制代码

如果数组成员允许包含多个类型值,则使用 (type1 | type2 | ...)[] 的语法声明。

// 此处定义了一个数组 `foo`,其成员可以是字符串,也可以是数值
let foo: (string | number)[] = ['Alex', 'Bob', 123];
复制代码

如果数组的成员是对象,则有如下两种声明方式:

// 方式 1:通过预定义好的类型,声明 `friends` 成员类型
interface Person {name: string;
}
let friends: Person[] = [ { name: 'Alex' }, { name: 'Bob' } ];// 方式 2:直接通过字面量类型的形式,声明 `friends` 成员类型
let friends: {name: string
}[] = [ { name: 'Alex' }, { name: 'Bob' } ];
复制代码

接下来,进入到扩展类型的学习。

扩展类型

Typescript 除了支持 JavaScript 类型之外,还提供了一些扩展类型。

首先,我们来介绍下字面量类型。

字面量类型

当我们像下面这样赋值时:

let seven: number = 7;
// ❌ 这样赋值的话会有错误,提示`Type '"Seven"' is not assignable to type 'number'`
seven = 'Seven';
复制代码

注意,这里的 'Seven' 被当成了一个类型,说 'Seven' 类型不能赋值给数值类型变量 seven

其实这里的 'Seven' 是一个字符串字面量类型。

Typescript 中的字面量类型包括:字符串字面量、数值字面量和布尔值字面量。

除此之外,我们还可以使用 type 关键字定义一个新的类型:

// 此处我们定义了一个新类型 `FavoriteNumber`,这个新类型仅由三个值的集合组成
type FavoriteNumber = 'One' | 'Two' | 'Seven';
// 接下来,将变量 `seven` 声明为类型 `FavoriteNumber`,并赋值为 `'Seven'`
let seven: FavoriteNumber = 'Seven';
复制代码

以上定义了一个类型 FavoriteNumber,它由三个值的集合组成(一个类型通常至少包含两个或以上的值)。变量 seven 被声明为该类型,赋值为 'Seven',这是一个有效值。如果我们给 seven 赋了一个不在 FavoriteNumber 类型之内的值,就会报错,比如:

// ❌ 此处会报错:`Type '123' is not assignable to type 'FavoriteNumber'.`
let seven: FavoriteNumber = 123;
复制代码

枚举

在介绍枚举类型之前,我们先来看下面的代码:

// 此处定义了两个变量 `errorColor` 和 `infoColor`
let dangerColor = 'red';
let infoColor = 'blue';// 添加一个判断传入颜色是否是危险色的函数
function isItDangerColor(color) {return color === 'red';
}// 接下来,调用函数 `isItRed`
isItDangerColor(dangerColor); // true
isItDangerColor(infoColor); // false
复制代码

上面这一小段的代码逻辑很简单,但有个小小的问题——如果表示危险的颜色由 'red' 变为 'pink' 了,那么我们就需要修改两个地方的代码。

针对这个问题,我们稍微修改下代码,引入一个表示颜色集合的变量 Colors 来解决:

// 我们使用 `Colors` 这个变量来存储逻辑中使用到的颜色集合
const Colors = {Danger: 'red',Info: 'blue'
};// 在余下的业务逻辑中,我们使用颜色变量代替之前的颜色字面值
let dangerColor = Colors.Danger;
let infoColor = Colors.Info;function isItDangerColor(color) {return color === Colors.Danger;
}
复制代码

这样带来的便利是,如果 Colors.Danger 所代表的颜色值变了,只要在 Colors 中修改一下就可以了。

进一步思考,可以知道,这里的 Colors.DangerColors.Info的值具体是什么并不重要,只要能保证它们彼此不相等就行。比如,我们写成下面这样:

// 这样定义 `Colors` 依旧不会影响逻辑
const Colors = {Danger: 0,Info: 1
};
复制代码

这种定义变量的方式,用 TypeScript 中的枚举来改写就是下面这样的:

// 枚举变量使用 `enum` 关键字定义
// 此处定义了一个枚举变量 `Colors`
enum Colors {Danger,Info
}
复制代码

上面一段代码经过编译后,得到的 JavaScript 源码如下:

var Colors;
(function (Colors) {Colors[Colors["Danger"] = 0] = "Danger";Colors[Colors["Info"] = 1] = "Info";
})(Colors || (Colors = {}));
复制代码

由此可知,

enum Colors {Danger,Info
}// 等价于var Colors = {0: 'Danger',1: 'Info','Danger': 0,'Info': 1
};
复制代码

我们修改下初始的例子,使用枚举来组织逻辑:

enum Colors {Danger, // 对应的值是 0Info, // 对应的值是 1Success // 对应的值是 2
}// 我们将函数 `isItDanger` 的参数 `color` 类型约束为 `Colors`
// 说明此函数只接收 `Colors` 中列举的值
function isItDanger(color: Colors): boolean {return color === Colors.Danger;
}// 接下来使用 `Colors.Info` 调用 `isItDanger` 函数
isItDanger(Colors.Info); // false
复制代码

除了使用默认的索引值,我们还可以为枚举变量中的每一项指定值:

enum Colors {Red, // 对应的值是 0Blue = 3, // 将 Blue 值指定为 3Green // 接上面的 3,此处的值是 4
}enum Colors {Red = 'red', // 对应的值是 'red'Blue = 'blue', // 对应的值是 'blue'Green = 'green' // 对应的值是 'green'
}
复制代码

上篇完。

TypeScript 从听说到入门(上篇)相关推荐

  1. typescript索引类型_typescript入门:高级类型

    学习typescript中 ,有一个小伙伴提出了一个问题 const a = { a:'1', b:'2', c:'3' } 复制代码 如何取到每个键上的值 ,组成一个联合类型 ? 即得到一个类型为 ...

  2. VTD场景搭建从听说到入门教程

    VTD作为自动驾驶主流仿真软件被各大主机厂与自动驾驶供应商广泛应用.但国内各大网站相关资料甚少,原厂文档又十分冗长.于是突发奇想决定试着写一个汉语精简版的VTD场景搭建的入门文章系列. 在实操之前让我 ...

  3. Vue+TypeScript使用教程-快速入门

    目前Vue CLI已经内置了TypeScript工具支持 一.使用 TypeScript 创建工程 vue create vue-ts 选择Manually Select Features 然后选择自 ...

  4. SQL查询入门(上篇)

    SQL语言是一门简单易学却又功能强大的语言,它能让你快速上手并写出比较复杂的查询语句.但对于大多数开发者来说,使用SQL查询数据库并没有一个抽象的过程和一个合理的步骤,这很可能会使在写一些特定的SQL ...

  5. Java加密解密快速入门上篇【包括MD5、BASE64、DES、RSA等算法】

    https://hello-nick-xu.iteye.com/blog/2103775

  6. Windows的图形设备接口(GDI)入门 上篇

    Windows图形设备接口(GDI)是为与设备无关的图形设计的.所谓设备的无关性,就是操作系统屏蔽了硬件设备的差异,使用户编程时无需考虑特殊的硬件设置. 我们利用GDI所提供的众多函数就可以方便的在屏 ...

  7. 数字孪生基本概念与入门-上篇

    大家好,这一期我们来聊一聊"数字孪生"的基本概念,大家这两年听到一个很火的词"元宇宙(Meteverse)",数字孪生也伴随元宇宙走入大众视野,从技术名词的角度 ...

  8. TypeScript入门教程一(通俗易通)

    typescript现在已经慢慢变成前端工程师必学必会的技能了,它在开发大型应用的时候可以让代码更加可控,下面我是整理一些typeScript的基本的入门教程. 一.原始数据类型 JavaScript ...

  9. OWIN的理解和实践(三) –Middleware开发入门

    原文:OWIN的理解和实践(三) –Middleware开发入门 上篇我们谈了Host和Server的建立,但Host和Server无法产出任何有实际意义的内容,真正的内容来自于加载于Server的M ...

最新文章

  1. 在eclipse使用map reduce编写word count程序生成jar包并在虚拟机运行的步骤
  2. Asp.Net 将HTML中通过dom-to-image.js标签div内的内容转化为图片保存到本地
  3. 清除vc6工程中的vss
  4. oracle 条件查询加排序,Java乔晓松-oracle的条件查询和排序查询
  5. python元组取值_Python基础之元组
  6. java菜单面板设置完能关闭_用Java创建一个屏幕外框架(或者:当所有应用程序窗口关闭时,如何避免Mac上的空白菜单)?...
  7. C++编译器默默编写并调用哪些函数
  8. 分享一套帮助你优化开发web应用的jQuery插件集 - Vanity Toolset
  9. steam一键授权工具_超实用小工具!一键将PC其它平台的游戏导入Steam
  10. 算法设计与分析(1)——基础知识
  11. 2k显示无法连接服务器,NBA2K18无法连接服务器怎么办 无法连接服务区解决方法一览...
  12. python残差神经网络_残差神经网络
  13. 解决Idea中项目模块没有蓝色方块
  14. CentOS7 修改Swap大小
  15. 解决PHP报错No input file specified
  16. 微信分享连接个别手机、ios转发不显示图片的,缩略图不出来
  17. Matlab R2012a/b反复激活无效+license checkout failed解决方案
  18. telnet不是内部或外部命令
  19. sublime Text 3 javaScript代码自动提示插件安装步骤
  20. DNS?本地填写的DNS有什么用?DNS怎么工作的?

热门文章

  1. 你是否对它有一种责任感
  2. Nmap (网络映射器)好东西啊
  3. Merge into的使用
  4. 转:ASP.NET状态保存方法
  5. 安装Python的wx库
  6. Linux I2C工具查看配置I2C设备【转】
  7. 小程序门店自提功能开启,酷客多带你玩转O2O模式
  8. Javascript 中的神器——Promise
  9. linux下bus、devices和platform的基础模型
  10. Hibernate flush理解