Ts在es 6基础上加了不少类型。同时还弄出了不少玩法。本章从基础数据类型开始讲起
注意:在ts中,定义数据类型,除了Function,其它全部都是开头小写。
在helloworld案例中,就已经使用了类型注解,相当于强类型语言中类型声明:
let greeting = (person: string) => `Hello, ${person}` // 参数person必须为字符串
基本的语法是:
(变量/函数):type
对于最基础的数据类型:
// 原始类型
let bool: boolean = true
let bool2:boolean = 'true' // 报错
let num: number = 123
let str: string = "123" // 等价于 let str = '';
如果你不按照规矩进行赋值,就要报错。
如果你要定义一个数组,你可以这么操作:
// 完全由数字组成的数组,二者等价
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
// 由数字 或 字符串组成的数组
let arr3: Array<number | string> = [1, "2", 3]
元组可以理解为是一种规定了数组长度和对应元素类型的特殊数组,它的定义也类似:
// 元组类型:限定了数组成员的类型和个数
let tuple:[number,string]= [1,'2']
let tuple: [number, string] = ["1", "2"] // 报错:Type 'string' is not assignable to type 'number'.
let tuple: [number, string] = [1, "2",3] // 报错:Types of property 'length' are incompatible.Type '3' is not assignable to type '2'.
但是,如果你对元组进行push,就不报错了。(实际开发强烈不建议这么骚操作)
let tuple: [number, string] = [1, "2"]
tuple.push(3) // 不报错
console.log(tuple) //[1,'2',3]
tuple[2] // 越界访问报错!
我们用es6的习惯写一个加法函数:
const add = (x, y) => x + y
这种实践是不好的,因为x,y都有可能是任何数据类型(any)。直接相加是要出问题的。所以解决方案是:
const add = (x: number, y: number) => x + y // 限定入参必须为数字
const add2 = (x: number, y: number): number => x + y // 限定返回值也必须为数字s
假如我们在ts中这么定义了一个对象:
const obj: object = {
x: 1,
y: 2,
}
obj.x = 3 // 警告:obj上未定义属性'x'
所以,你应该写明白obj上x和y的属性:
const obj: { x: number; y: number } = { x: 1, y: 2 }
obj.x = 3
symbol的含义就是:具有唯一的值:
const s1: symbol = Symbol()
const s2 = Symbol()
console.log(s1 === s2) // false
在ts的定义中,undefined和null是任何其它数据类型的子类型,按理来说类似这种操作是应该允许的:
let aaa: number = 1
aaa = undefined
结果报错了。这时就应该去配置项tsconfig
中设置strictNullChecks:false
let aaa: number|undefined = 1
aaa = undefined
在js中你可以声明undefined
这个变量。
// 浏览器环境下
(function(){
const undefined = 0;
console.log(undefined);
})()
// 0
那么在这个闭包中,undefined就是0。
js中void是一种操作符,在ts中,返回真正的undefined
。
还有一个很出名的就是any类型,表示被赋值什么类型都可以。(实现了js的功能。不建议)
never类型:一下两种情况,返回never,表示永远不会有返回值:
const err = () => {
throw new Error("err")
}
const endless = () => {
while (true) {}
}
假设有一个用户登录系统,根据角色来显示不同的界面。我们用传统js的思维写一段判断角色的逻辑:
const initByRole = role => {
if (role === 1 || role === 2) {
// do sth
} else if (role == 3 || role == 4) {
// do sth
} else if (role == 5) {
// do sth
} else {
// do sth
}
}
这段代码问题明显:
1.可读性极差:没有文档基本没人敢改。2.硬编码问题:如果要改,改动肯定很大。
如果到了ts,可以用枚举类型来处理这种场景。
所谓枚举类型者,就是一组有名字的常量组合。比方说手机通讯录:
•张三->10086•李四->10010
我打电话给张三时,直接从通讯录检索器名字即可,很少一个个按键拨电话。当然你想通过电话来找人,也是可以的。
在ts中,可以使用数字枚举来定义5个角色的映射
// 数字枚举
enum Role {
Reporter, // 0
Developer, // 1
Maintainer, // 2
Owner, // 3
Guest, // 4
}
// Role.Reporter -> 0
// Role[0] -> Reporter
有了枚举类型,你可以通过属性来索引,也可以通过值来索引。
如果你尝试打印,发现这些其实都是普通的的Object对象:
它的编译过程是:
var Role;
(function (Role) {
Role[Role["Reporter"] = 0] = "Reporter";
Role[Role["Developer"] = 1] = "Developer";
Role[Role["Maintainer"] = 2] = "Maintainer";
Role[Role["Owner"] = 3] = "Owner";
Role[Role["Guest"] = 4] = "Guest";
})(Role || (Role = {}));
就是反向映射。
对于枚举类型,完全可以人工定义:
enum Msg {
success = "成功",
Fail = "失败",
}
再看看编译结果:
var Msg;
(function (Msg) {
Msg["success"] = "\u6210\u529F";
Msg["Fail"] = "\u5931\u8D25";
})(Msg || (Msg = {}));
显然无法做到根据"成功"索引success——也就是说,对于字符串枚举,是没法做到反向映射的。
当然你可以字符串和数字放在一个枚举对象,称之为异构枚举。
enum Aaa {
a = 1,
b = "1",
}
编译结果
(function (Aaa) {
Aaa[Aaa["a"] = 1] = "a";
Aaa["b"] = "1";
})(Aaa || (Aaa = {}));
很明显这种操作很容易混淆,也是不建议使用的。
•枚举成员的值,是不能修改的。(只读)•对于枚举类型,定义值有几种情况:•没有定义值。•对其它枚举属性的引用。•常量表达式:编译时被计算出来。•非常量表达式:如arr.length:编译时被保留•常量枚举:比如
const enum Month{
Jan=1,
Feb=2,
Mar=3
}
是不会被编译的。那又有什么意义呢?
可以引用常量枚举的表达式,虽然编译不存在这个Month对象,但通过常量枚举,减少了代码的复杂度。
const enum Month {
Jan = 1,
Feb = 2,
Mar = 3,
}
let month = [Month.Jan, Month.Feb, Month.Mar]
console.log(month)
编译结果为:
var month = [1 /* Jan */, 2 /* Feb */, 3 /* Mar */];
console.log(month);
•枚举类型和枚举成员都可以作为类型来定义。枚举类型不同的枚举值,不可项目比较。