几个月前把 ES6 的特性都过了一遍,收获颇丰。现在继续来看看 TypesScript(下文简称为 “TS”)。限于经验,本文一些总结如有不当,欢迎指正。

概述

官网有这样一段描述:

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

说的是 TS 是 JS 的超集,并且可以编译成普通的 JS。

其中, 超集 的定义是:

如果一个集合 S2 中的每一个元素都在集合 S1 中,且集合 S1 中可能包含 S2 中没有的元素,则集合 S1 就是 S2 的一个超集,反过来,S2 是 S1 的子集。

而实际上,“超出” 的部分主要就是 “类型系统”。因此可以这样归纳:

TS ≈ ES6 + 类型系统

ES6 是 ES5 转向主流语规格的一个重要升级,顺着这个角度看,TS 让这门语言披上一层类型的外衣,直接演变成一种强类型的语言;从相反角度看,TS 将编程语言们一些主流的特性引入到了 JS 的世界。

平稳过渡

TypeScript 设计巧妙,兼具微软工业化的水准。首先,它仅靠一行命令,就融入到了广大前端人的世界:

npm install -g typescript

然后由你随便挑一个曾编写的 .js 脚本文件(不妨叫做hello.js),不用对内容做任何修改,直接将文件后缀改成 .ts。这样,你就已经完成了一份 TypeScript 脚本的编写!

然后编译它:

tsc hello.ts

OK,你已经平滑过渡到了 TS 的世界。就是这么简单!

当然这只是“一小步”,似乎后边还有无数的坑要填。不用担心,TS 已经填平了大部分的坑!

比如,时下最流行的 gulp,webpake 工具,只需做一些简单的配置,就能接引入TypeScript 进行编译;同时为了能与 React 完美融合,TS 引入了与 JSX 类似的 TSX 语法。当然,TS 在 Angular、Vue.js 以及 Node.js 中也是畅通的......

坑都填平了,大家过渡起来自然顺心顺手。

基本类型

与 ES6 一脉相承的,同时也接轨大部分强类型语言,TS 的类型大概有这些:
1),NumberBooleanStringNullundefinedSymbol
2), ArrayFunctionObject
3),TupleenumVoidNeverAny
TS 作为 JS 的一个超集,在 JS 的基础上扩展了一些非常有用的类型。第 3)中的类型就是从一些强类型语言引入的类型。

为了由简入繁,不妨将这些类型划分为:基本类型复合类型复合类型 一般由 基本类型 构成。以下将渐进式的对 TS 的这些类型进行了解。

如何做类型声明?

强类型语言都有一套类型声明的语法规则,TS 也不例外。TS 采用类型注释的写法,像这样将一个带冒号的注释,置于声明变量名之后,就构成了 TS 类型声明的语法

let str : string = 'hello typescript';

JAVA 的写法是相反的,但无实质差别:

String str = 'hello java';

这样的注释如同一种补充说明,后文将简称它为 “冒号注释”,熟悉书写规则,有利于快速进入到 TS 的代码世界。

实际上,ES6 有一种属性描述对象,是通过Object.getOwnPropertyDescriptor(obj, key) 获取的。

let obj = {set name(val) {}
}
Object.getOwnPropertyDescriptor(obj, 'name');
// {
//      configurable: true
//   enumerable: true
//   get: undefined
//   set: ƒ a(val)
// }

如果将 setter 类型的 name 方法适当改写,我们甚至可以实现 obj.name 赋值的类型检查功能,也非常有意思。

同样的,冒号注释 : string 也可以理解为对一个 str 变量的描述。凭借这个注释的描述,TS 的类型编译器就能进行类型检查了。

建立了类型的认知后,继续跑马圈地,巩固认知。其中,FunctionNeverAny 规则稍显复杂,但也没有什么特别的,留后细说。

简单的基本类型

// boolean 类型
let isBool: boolean = 1 < 5;// string 类型
let str: string = 'hello world';// number 类型
let num: number = 123;// void 类型
let unusable: void = undefined;// undefined 类型
let u: undefined = undefined;// null 类型
let n: null = null;//Symbol 类型
// 类型 symbol 小写也能编译通过
let sym: Symbol = Symbol('hello');

简单的复合类型。

// object 类型
let obj : object = {};
let arrObj : object = [];
let funcObj : object = () => {};// array 类型
let arrNum : number[] = [1, 2, 3]
let arrStr : string[] = ['a', 'b', 'c']
let arrObj : object[] = [{}];// 元组 类型
let tup : [number, string] = [1, 'hello'];// 枚举类型
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

可谓一览无余,类型的语法就是冒号注释,仅凭这一条,60~70% 的情况你都无需担心自己的类型书写有误。

一个灵活的子类型

但 JS 的动态类型太灵活了,nullundefined 的相似性, ArrayFunctionObject的纠缠不清的关系,仅凭一招恐怕还很难驾驭的住 JS 的 “多动症” 般的类型。比如:

// boolean 类型接收这样的赋值
let isBool_n: boolean = null;
let isBool_u: boolean = undefined;// void 类型接收这样的赋值
let unusable: void = undefined;
unusable = null;// Symbol 类型接收这样的赋值
let sym: Symbol = Symbol('hello');
sym = null;
sym = undefined;// object 类型接收这样的赋值
let obj : object = {};
obj = null;
obj = undefined;

它们都能编译通过。但是 null 不属于 boolean 类型,undefined也并不属于object 类型,为什么能通过类型检查?

事实上,undefinednull 是所有类型的子类型。也就是说,它们俩可以作为值赋给任何类型的变量。甚至,它们俩可以互相赋值给对方。

// undefined 类型
let u: undefined = null;// null 类型
let n: null = undefined;

有了这一条规则,就能解释一些 “复合类型” 中遇到的问题:

let arrNum: number[] = [];
let arrStr: string[] = [];// undefined 也属于 number 类型
let arrNum: number[] = [undefined];// undefined 也属于 object 类型
let obj : object = undefined;

有了这条规则,我们可以大胆的写 TS 的类型声明了。

但太过放开的规则——本文姑且称之为 “混杂模式”,又似乎一下子让 TS 退回到了 JS 的动态类型的原始状态了,让习惯了强类型的同学容易懵掉,也让从 JS 转 TS 的同学体会不到强类型的好处。

画条界限

好在,TS 设计了一套巧妙的类型系统,犹如给 JS 披上 了一层强大的盔甲。

TS 在 “混杂模式” 下,可能存在这样的风险,就是:编译正确,运行出错。比如:

// 无意获得一个 undefined 作为初始值
let init_name = undefined;
let nameList: string[] = [init_name];
console.log(nameList[0].split('_')); // 运行报错

在非 “严格模式” 下,上述 TS 代码编译无误,但是真正拿到页面去运行编译结果时,出现错误。

那怎么办呢?要相信 TS 强大的类型系统,只需一项配置,就能将编译切换成 “严格模式”:

// 在配置文件 tsconfig.json 中增加一项
"compilerOptions": {// ..."strictNullChecks": true},

再次执行编译,就会出现错误提示信息:

error TS2322: Type 'undefined[]' is not assignable to type 'string[]'.

TypeScript 官方教程鼓励尽可能地使用 --strictNullChecks,因此这里也强烈建议配置该属性再进行编译,这样能很好的发挥 TS 类型检查的作用。

网开一面和漏网之鱼

TS 编译通过指的是类型检查符合类型系统的规则,运行 OK 则是编译后的 JS 本身执行无误。编译通过,不等于运行OK,即使在 “严格模式” 下也是这样的,所以千万别以为编译通过了就完事了。

Any 类型为例,在 --strictNullChecks 模式下:

// TS 代码
let anyThing: any = 'hello';
console.log(anyThing.myName);// 编译后的 ES6
let anyThing = 'hello';
console.log(anyThing.setName('world'));

很显然,编译后的 anyThing.setName('world') 会运行报错。

当然, Any 类型略有点特殊,因为它可以当做是 TS 平滑退化到 JS 的一个类型,官网教程也有这样解说:

在对现有代码进行改写的时候,any类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。

那问题又回来了,是否除了 Any 类型,其他编译OK,代码就运行无错呢?鉴于笔者正在入门,经验有限,不敢给这个结论。但无论如何,类型检查是可以排除大部分错误的。

最后,编译的时候,尽量选择编译成 ES6 (前提是项目是用 ES6 写的)。配置是:

"compilerOptions": {"target": "es6" // "es5"
}

只有一条规则

TS “冒号注释” ——就这一条规则,贯穿始终。在函数的类型声明中,继续来巩固这条规则的写法。

类型声明只对变量负责,对于函数,需考察输入——函数参数(也是变量)、输出——函数返回值两个要素。

因为函数的特殊结构,所有 “冒号注释” 规则的写法要特别了解下:

// 声明函数
function add(x: number, y: number): number {return x + y;
}// 函数直接量
let myAdd = function(x: number, y: number): number { return x + y; };

可以看到,参数的 “冒号注释” 和一般变量没有任何差别。倒是函数输出类型注释有点特别——试想,: number 紧随函数名之后或者 function 关键字之后,是不是容易被误解为函数名的一部分?是不是对编译引擎不太友好?从这个角度看,注释置于) 之后最为合理。

对了,一个疑问一直从头保留到现在:要是一个变量是 function 类型,那类型注释怎么写,又不能拿 function 关键字去做类型注释?

let myAdd: (x: number, y: number) => number =function(x: number, y: number): number { return x + y; };

其中等号前边的 : (x: number, y: number) => number 就代表了函数类型。它仍然在结构上符合 “冒号注释” 的规则,只不过冒号后边是一串表达式。这样的结构有点像 Python 中的推导式的概念。

好了,补上这一块重要的缺漏,本文就完成了所有基本类型的类型声明的解释。凭借一条规则,希望在 TS 学习上畅通无阻的敲代码~

JavaScript = TypeScript 入门相关推荐

  1. JavaScript = TypeScript 类入门

    尝试重写 在此之前,通过<JavaScript => TypeScript 入门>已经掌握了类型声明的写法.原以为凭着那一条无往不利的规则,就可以开开心心的重写 JS 项目了.当我跃 ...

  2. 教你用Vue渐进式搭建聊天室,从JavaScript=TypeScript

    前言 Vue+Socket.io这个轮子已经有很多人造过了,为了不重复造轮子,我将本项目以三阶段实现(大家可以在github中的Releases查看): 纯前端(Vuex) 后端+前端(JavaScr ...

  3. 我写的第一本书《TypeScript 入门教程》

    阅读原文 持续了大半年的学习和写作,在今天终于告一段落了. 写书之旅 最初有写书的想法,是刚加入微软的时候. 由于工作中需要重度使用 TypeScript,所以我花了几天的时间研读了好几遍官方手册和中 ...

  4. TypeScript入门教程 之 classes-emit

    TypeScript入门教程 之 classes-emit What's up with the IIFE 为该类生成的js可能是: function Point(x, y) {this.x = x; ...

  5. TypeScript入门教程 之 箭头函数

    TypeScript入门教程 之 箭头函数 亲切地称为粗箭头(因为->是细箭头并且=>是粗箭头),也被称为lambda函数(由于其他语言).另一个常用功能是胖箭头功能()=>some ...

  6. TypeScript入门教程 之 类/抽象类/构造器/Getter/Setter

    TypeScript入门教程 之 类/抽象类/构造器/Getter/Setter 将JavaScript中的类作为头等项很重要的原因是: 类提供了有用的结构抽象 为开发人员提供一种一致的方式来使用类, ...

  7. TypeScript入门教程 之 解构

    TypeScript入门教程 之 解构 TypeScript支持以下形式的解构(以解构的名义命名,即分解结构): 对象分解 阵列解构 人们很容易将解构视为结构的逆.JavaScript中的结构化方法是 ...

  8. TypeScript入门教程 之 枚举 Enums

    TypeScript入门教程 之 枚举 Enums 枚举是一种组织相关值集合的方法.许多其他编程语言(C / C#/ Java)具有enum数据类型,而JavaScript没有.但是,TypeScri ...

  9. TypeScript入门教程 之 for ... of 与 for ... in

    TypeScript入门教程 之 for ... of 与 for ... in 新手JavaScript开发人员经常遇到的错误是,for...in数组不会遍历数组项.相反,它迭代传入的对象的键.下面 ...

  10. TypeScript入门教程 之 生成器函数

    TypeScript入门教程 之 生成器函数 生成器函数 function *是用于创建生成器函数的语法.调用generator函数将返回一个generator对象.发电机对象如下刚刚所述迭代器接口( ...

最新文章

  1. Linux的概念与体系
  2. 写了个数字转换为中文大写的软件
  3. 关于工具类应用产品界面设计的一点思考
  4. Python 内编写类的各种技巧和方法
  5. {dede:list}和{dede:arclist}的区别
  6. Apple 远程推送APNS 服务
  7. 我给这个Python库打101分!
  8. Just write about
  9. C语言试题九十之实现输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数。
  10. ​全网首发,TensorFlow 2.0 中文视频教程来啦
  11. coupled/decoupled
  12. 信息论常见概念:熵、互信息、KL散度和信息增益
  13. at for lte
  14. centos7修改ip地址命令_每天一条Linux命令(12) hostnam (超详细)
  15. skywalking调研相关资料整理
  16. C语言把二进制转换为十进制数的方法和示例
  17. mysql 左连接都是null_sql左连接有疑问,为什么查出来都是null,而不是没有数据?...
  18. 微生物增殖java_Java实现微生物增殖
  19. 元组与字典——python
  20. Java设计模式全集-工厂模式系列(Factories)

热门文章

  1. 在Windows Server2012系统中安装Oracle11g
  2. 程序员应该练练太极拳
  3. win7_fedora 双系统安装方法
  4. SQL*Loader 和 Data Pump
  5. XLua访问C#中的List或者数组
  6. 双击事件 转载 http://blog.sina.com.cn/s/blog_739365a30100vk8p.html
  7. mysql 批量数据循环插入
  8. python基础知识(day3)
  9. odi 12.2.1.1新特性
  10. PHP------- 继承