TypeScript就是具有静态类型的JavaScript。
什么是静态类型?
语言可以分为动态类型语言和静态类型语言两种。
动态类型语言就是程序运行期间才做数据类型检查的语言,如:JS。
静态类型语言就是程序编译期间做数据类型检查的语言,如:Java。
静态类型语言在编译期间就可以发现一些潜在的错误,避免程序在生产环境运行了之后再发现错误。
而,TypeScript就是具有静态类型特征的JavaScript。
浅谈一下类型系统
什么是类型系统
类型系统包含两个重要的组成部分:
- 类型标注(定义、注解) - typing
- 类型检测(检查) - type-checking
类型标注
类型标注就是在代码中数据添加类型说明,当一个变量或者是函数(参数)等被标注之后就不能存储或者传入与标注类型不符合的数据。
有个类型标注,编译器才能进行类型检测进行智能提示。
TS的类型标注基本语法格式为数据载体:类型。
类型检测
类型检测就是对数据类型进行检测。类型系统是对数据的类型进行检测,而不是对数据的具体值进行检测。
关于TS的类型标注,我们一般分为两种:
- 基础的简单的类型标注
- 高级的深入的类型标注
基础的简单的类型标注
基础的简单的类型 分为很多种类:
- 基础类型
- 空和未定义类型
- 对象类型
- 数组类型
- 元组类型
- 枚举类型
- 无值类型
- Never类型
- 任意类型
- 未知类型
基础类型
基础类型包括:string、number、boolean。
比如:
let str: string = "Hello";
let n: number = 100;
let isTrue: boolean = true;
空和未定义类型
由于null和undefined两种类型有且只有一个值,所以,在标注一个空和未定义类型的时候,就表示该变量不能被修改。
let a: null;
// ok
a = null;
// error
a = 1;
默认情况下,null和undefined是所有类型的子类型。也就是说,可以将null和undefined作为其他类型的变量。
let a: number;
// ok
a = null;
如果一个变量声明了却没有赋值,默认该变量为的值undefined。
Tips
由于
null和undefined都可以作为其他类型的子类型,则在某些情况下可能会存在一些隐藏的问题。比如说:声明的变量(number)a赋予null值,而a却可以使用toFixed,而这样会造成程序上的错误。
我们可以通过指定
strickNullCheck为true,就可以有效的检测null和undefined,从而避免问题。
🌟 对象类型
内置对象类型
内置对象,就是比如说:Date、Array、Object...之类的类型,可以通过对象的构造函数或者类来进行标注。
let a: object = {};
let arr: Array<number> = [1, 2, 3];
let d: Date = new Date();
自定义对象类型
我们经常性的需要使用一些自定义结构的对象,对于自定义的对象我们可以通过 字面量标注、接口、定义类或者构造函数来实现。
字面量标注
let obj: {username: string; age: number} = {
username: 'xx',
age: 35,
};
// ok
console.log(a.username, a.age);
// error
console.log(a.gender);
字面量标注 方便、直接 但是 不利于复用和维护。
🌟 接口 interface
接口interface是TS中很重要的一部分,后续再展开讨论,这里先用在对象类型中。
// interface关键字
interface Person {
username: string;
age: number;
};
let a: Person = {
username: 'xx',
age: 53,
};
// ok
console.log(a.username, a.age);
// error
console.log(a.gender);
let user = Person; // 不能当作实体使用
接口定义对象 复用性高 但是 不能作为具体的值使用,只是一种抽象的结构定义,并不是实体。
定义类 或者 构造函数
class Person {
constructor(public username: string, public age: number) {}
}
// ok
console.log(a.username, a.age);
// error
console.log(a.gender);
let user: Person = new Person('xx', 21);
用类 或者 构造函数 定义对象 定义了对应的类型 但是 比较复杂且没有必要。
还有一种包装对象,比如说
String、Boolean,这些包装对象和基础类型是不一样的。包装对象有的基础类型可能并没有。
🌟 数组类型
使用泛型标注
// <number> 表示数组中存储的数据类型
let arr: Array<number> = [];
// ok
arr.push(100);
// error
arr.push('xx');
简单标注
let arr: string[] = [];
// ok
arr.push('xx');
// error
arr.push(1);
元组类型
元组与数组类似,只是存储的元素类型不相同。 元组类型中可以存放不同的类型的数据,但是在标注的时候,元组类型必须要注意:
- 初始化元组类型数据的时候必须要和对应位置的类型和个数保持一致
- 越界数据必须要是元组类型标注中的类型之一
let yarr: [string, number] = ["xx", 100];
// ok
yarr.push(100);
yarr.push("100");
// error
yarr.push(true);
枚举类型
枚举类型就是将一组有关联意义的数据整合在一起赋予方便使用的名字,比如我们在封装HTTP请求时需要使用到的一些状态,但是这些数据在初始化之后是不能修改的:
enum HTTP_CODE {
OK = 200,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED
};
// 200
console.log(HTTP_CODE.OK);
// 405
console.log(HTTP_CODE.METHOD_NOT_ALLOWED);
// error
HTTP_CODE.OK = 1;
枚举类型在标注的时候还有一些比较重要的注意项:
- key不可以是数字;
- value可以是数字(数字类型枚举)或者是字符串(字符串类型枚举),默认数字是0;
- value可以省略,但是,第一个省略的值默认是0,或者是上一个数字枚举值+1;
- value为只读。
无值类型
没有返回任何数据的类型为无值类型(void)。也就是,没有return 或者是 return undefined。
function fn(): void {
// 没有 return 或者 return undefined
}
Tips
在
strictNullChecks为false的情况下,undefined和null都可以赋值给void,但是当strictNullChecks为true的情况下,只有undefined才可以赋值给void
Never类型
函数永远不可能执行return时,为Never类型。比如说抛出错误导致终止运行的函数:
function fn(): never {
// 抛出错误 无return
throw new Error('error');
}
🌟 任意类型 any
当我们不知道该值为何值(函数或者是对象) 或者 不需要给该值进行检测 时,可以考虑使用any来标注。
Tips
noImplicitAny为true时,可以在函数参数出现隐含的any时报错
未知类型 unknow
unknow属于安全版的any。
any可以使用任何方法和属性,而unknow没有任何方法和属性。而且unknow只能赋值给unknow 和 any。
🌟 函数类型
在TS中,函数标注非常重要,函数拥有多种自己的类型标注格式(自定义对象类型也是)。
可以使用interface 和 type 来进行标注,不过,可以先看下普通的简单的标注形式:
函数名称(参数1: 类型, 参数2: 类型...): 返回类型
function add(x: number, y:number): number {
// 参数: 类型 => 返回类型
return x + y;
}
Tips
我觉得简单的类型标注可以分为这几种,然而,我们还可以通过接口interface 、 类型别名type 来对这些类型进行一些方便的标注。同时,类型系统还存在一些高级的深入的类型标注。