在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 什么是TypeScript? TypeScript是一种添加了类型系统的 JavaScript,适用于任何规模的项目。 我们都知道JavaScript是一种弱类型的语言。而TypeScript增强了它的类型。 由于JavaScript 是一门非常灵活的编程语言,这将导致:
这种灵活性就像一把双刃剑,一方面使得 JavaScript 蓬勃发展,无所不能,从 2013 年开始就一直蝉联最普遍使用的编程语言排行榜冠军;另一方面也使得它的代码质量参差不齐,维护成本高,运行时错误多。 而 TypeScript 的类型系统,在很大程度上弥补了 JavaScript 的缺点。它是静态语言。 而JavaScript是动态语言,如下面的代码直到运行时才会报错: let foo = 1;
foo.split(' ');
同样是这段代码,在TypeScript下编译时就会报错了。
你可能会奇怪,这段 TypeScript 代码看上去和 JavaScript 没有什么区别呀。没错!大部分 JavaScript 代码都只需要经过少量的修改(或者完全不用修改)就变成 TypeScript 代码,这得益于 TypeScript 强大的[类型推论][],即使不去手动声明变量 完整的 TypeScript 代码是这样的: let foo: number = 1;
foo.split(' ');
TypeScript 是弱类型: 类型系统按照「是否允许隐式类型转换」来分类,可以分为强类型和弱类型。 以下这段代码不管是在 JavaScript 中还是在 TypeScript 中都是可以正常运行的,运行时数字 console.log(1 + '1'); 作为对比,Python是强类型的。需要进行强制类型转换。 这样的类型系统体现了 TypeScript 的核心设计理念:在完整保留 JavaScript 运行时行为的基础上,通过引入静态类型系统来提高代码的可维护性,减少可能出现的 bug。 TypeScript非常适合于大型项目。在中小型项目中推行 TypeScript 的最大障碍就是认为使用 TypeScript 需要写额外的代码,降低开发效率。但事实上,由于有[类型推论][],大部分类型都不需要手动声明了。TypeScript 还可以和 JavaScript 共存。这意味着如果你有一个使用 JavaScript 开发的旧项目,又想使用 TypeScript 的特性,那么你不需要急着把整个项目都迁移到 TypeScript,你可以使用 TypeScript 编写新文件,然后在后续更迭中逐步迁移旧文件。如果一些 JavaScript 文件的迁移成本太高,TypeScript 也提供了一个方案,可以让你在不修改 JavaScript 文件的前提下,编写一个[类型声明文件][],实现旧项目的渐进式迁移。 事实上,就算你从来没学习过 TypeScript,你也可能已经在不知不觉中使用到了 TypeScript——在 VSCode 编辑器中编写 JavaScript 时,代码补全和接口提示等功能就是通过 TypeScript Language Service 实现的。 一些第三方库原生支持了 TypeScript,在使用时就能获得代码补全了,比如 Vue 3.0 有一些第三方库原生不支持 TypeScript,但是可以通过安装社区维护的类型声明库(比如通过运行
安装TypeScript TypeScript 的命令行工具安装方法如下: npm install -g typescript 以上命令会在全局环境下安装 编译一个 TypeScript 文件很简单: tsc hello.ts 我们约定使用 TypeScript 编写的文件以 Hello TypeScript 我们从一个简单的例子开始。 将以下代码复制到 function sayHello(person: string) { return 'Hello, ' + person; } let user = 'Tom'; console.log(sayHello(user)); 然后执行 tsc hello.ts 这时候会生成一个编译好的文件 function sayHello(person) { return 'Hello, ' + person; } var user = 'Tom'; console.log(sayHello(user)); 上述例子中,我们用 这是因为 TypeScript 只会在编译时对类型进行静态检查,如果发现有错误,编译的时候就会报错。而在运行时,与普通的 JavaScript 文件一样,不会对类型进行检查。 如果我们需要保证运行时的参数类型,还是得手动对类型进行判断: function sayHello(person: string) { if (typeof person === 'string') { return 'Hello, ' + person; } else { throw new Error('person is not a string'); } } let user = 'Tom'; console.log(sayHello(user));
下面尝试把这段代码编译一下: function sayHello(person: string) { return 'Hello, ' + person; } let user = [0, 1, 2]; console.log(sayHello(user)); 编辑器中会提示错误,编译的时候也会出错。 但是还是生成了 js 文件。 这是因为 TypeScript 编译的时候即使报错了,还是会生成编译结果,我们仍然可以使用这个编译之后的文件。 如果要在报错的时候终止 js 文件的生成,可以在 本部分介绍了 TypeScript 中的常用类型和一些基本概念,旨在让大家对 TypeScript 有个初步的理解。具体内容包括: 原始数据类型 JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。 原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt。 本节主要介绍前五种原始数据类型在 TypeScript 中的应用。 布尔值 数值 字符串 空值 Null 和 Undefined 任意值任意值(Any)用来表示允许赋值为任意类型。 什么是任意值类型? 如果是一个普通类型,在赋值过程中改变类型是不被允许的。但如果是 任意值的属性和方法: 在任意值上访问任何属性都是允许的。 也允许调用任何方法。 可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。 未声明类型的变量 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型。 类型推论如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。 什么是类型推论 以下代码虽然没有指定类型,但是会在编译的时候报错: let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。 联合类型联合类型(Union Types)表示取值可以为多种类型中的一种。 简单的例子 let myFavoriteNumber: string | number; myFavoriteNumber = 'seven'; myFavoriteNumber = 7; 访问联合类型的属性或方法 对象的类型——接口在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。 什么是接口 在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。 TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。 简单的例子 interface Person { name: string; age: number; } let tom: Person = { name: 'Tom', age: 25 }; 上面的例子中,我们定义了一个接口 可选属性 有时我们希望不要完全匹配一个形状,那么可以用可选属性。 任意属性 有时候我们希望一个接口允许有任意的属性,可以使用如下方式。 interface Person { name: string; age?: number; [propName: string]: any; } 只读属性 有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性。 数组的类型在 TypeScript 中,数组类型有多种定义方式,比较灵活。 「类型 + 方括号」表示法 数组泛型 用接口表示数组 接口也可以用来描述数组: interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了。 不过有一种情况例外,那就是它常用来表示类数组。 类数组 类数组(Array-like Object)不是数组类型,比如 function sum() { let args: number[] = arguments; } 上例中, function sum() { let args: { [index: number]: number; length: number; callee: Function; } = arguments; } 在这个例子中,我们除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有 事实上常用的类数组都有自己的接口定义,如 关于内置对象,可以参考内置对象一章。
|
请发表评论