typescript学习(一)
官网的介绍为:TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。实际上就是规定了一种强类型的写法,加入了一个将ts代码编译为js代码的过程,可以在编译过程中及时发现错误,而不是等到运行时在页面上瞎鸡儿点,然后发现是变量类型错误,变量名写错这种糟心的bug。这种的Uncaught TypeError
就很影响写代码时的心情。
安装与使用
TypeScript 的命令行工具安装方法如下:
npm install -g typescript
以上命令会在全局环境下安装 tsc
命令,安装完成之后,我们就可以在任何地方执行 tsc
命令了。
编译一个 TypeScript 文件很简单:
tsc hello.ts
我们约定使用 TypeScript 编写的文件以 .ts
为后缀
优势与不足
TypeScript 增加了代码的可读性和可维护性
- 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了
- 可以在编译阶段就发现大部分错误,这总比在运行时候出错好
- 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等
TypeScript 非常包容
- TypeScript 是 JavaScript 的超集,
.js
文件可以直接重命名为.ts
即可 - 即使不显式的定义类型,也能够自动做出类型推论
- 可以定义从简单到复杂的几乎一切类型
- 即使 TypeScript 编译报错,也可以生成 JavaScript 文件
- 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取
TypeScript 的缺点
任何事物都是有两面性的,我认为 TypeScript 的弊端在于:
- 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念
- 短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript 能够减少其维护成本
- 集成到构建流程需要一些工作量
- 可能和一些库结合的不是很完美
基本使用
JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。
原始数据类型包括:布尔值、数值、字符串、null
、undefined
以及 ES6 中的新类型 Symbol
。
基本类型
boolean
布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean
定义布尔值类型:
//编译通过
let isDone: boolean = false;
//使用构造函数生成的不是boolean值,而是一个对象
let createdByNewBoolean: boolean = new Boolean(1);
//可以使用Boolean方法返回一个boolean值
let createdByBoolean: boolean = Boolean(1);
数值
***
字符串
***
空值
JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void
表示没有任何返回值的函数:
function alertName(): void {
alert('My name is Tom');
}
声明一个 void
类型的变量没有什么用,因为你只能将它赋值为 undefined
和 null
:
let unusable: void = undefined;
Null 和 Undefined
在 TypeScript 中,可以使用 null
和 undefined
来定义这两个原始数据类型:
let u: undefined = undefined;
let n: null = null;
与 void
的区别是,undefined
和 null
是所有类型的子类型。也就是说 undefined
类型的变量,可以赋值给 number
类型的变量:
// 这样不会报错
let num: number = undefined;
// 这样也不会报错
let u: undefined;
let num: number = u;
any类型
我们知道typescript是强类型的,因此是不允许在赋值过程中改变变量的类型,若变量的类型在程序运行过程中真的需要改变,那么我们可以将其定义为any类型
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
对于未显式声明类型且未进行初始化的变量,它默认会被指定为any
let something;
something = 'seven';
something = 7;
对于未显式对于未显式声明类型但是进行过初始化的变量,会进行类型推断
let myFavoriteNumber = 'seven';
//事实上等同于
let myFavoriteNumber: string = 'seven';
联合类型
我们也可以对一个变量指定多个类型,在赋值时,ts也根据所赋值进行类型推断
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
并且我们可以访问这些类型的公共方法或者属性
function getString(something: string | number): string {
return something.toString();
}
Interfaces类型
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
在使用定义的接口类型时,性新对象内少一些或者多一些属性都是不被允许的,有时我们希望不要完全匹配一个接口,那么可以用可选属性
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom'
};
若我们需要添加接口内没有的属性,那么可使用任意属性,
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
interface Person {
name: string;
age?: number;
[propName: string]: string;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
此时会报错,age会被先于任意属性做匹配,此时需要的是string类型,可以该为下面这种用法
interface Person {
name: string;
age?: number;
[propName: string]: string | number;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
只读属性
有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly
定义只读属性:
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527;
// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property
需要注意的是只读属性必须在给对象第一次赋值的时候进行设置
数组类型
简单用法
//数组内只允许存入数值
let fibonacci: number[] = [1, 1, 2, 3, 5];
泛型用法
let fibonacci: Array = [1, 1, 2, 3, 5];
接口用法(不推荐)
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
需要注意的是类数组不是数组,它是一个区别于普通数组的对象
函数类型
函数声明
function sum(x: number, y: number): number {
return x + y;
}
函数表达式
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
值得注意的是我们不止要对函数做出限制,也要对承载函数的那个变量做出限制,这里使用的=>
与ES6中的=>
是两码事。在 TypeScript 的类型定义中,=>
用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
使用接口定义函数
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
可选参数与默认值
function buildName(firstName: string='ccc', lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
需要注意的是:可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了:
但是在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数:但是这种可选参数就不受可选参数必须接在必需参数后面约束了
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');