今天继续来讲述TypeScript中与类型有关的东西,我个人想把我觉得概念很相近的东西放到一起来讲,所以得打乱我之前学习的顺序了。
关于那些“奇奇怪怪”的类型
这里我说“奇怪”,并不是觉得“有问题,不对劲”的意思,而是觉得“神奇而有趣”——ts既不像静态类型语言一样固定和单一,又不像JavaScript那样太过灵活而毫无限制,可以说是“适当的灵活”了.。我在学习的时候常常有这样的感叹“啊,这样也可以的吗?”我在这里整理出一些类型相关的知识点,可能你会觉得乱序。
1.带类型的数组
我们都知道,ts里需要在变量名后面加上:类型名来指定类型。例如,let a: number = 0。假如我要定义的是数组呢,并且是专门存放数字的数组(如一组同学的分数),类型应该写成什么样呢?
可以这样写:
const arr: number[] = [1, 2, 3]
也就是写成:数组元素的类型[]即可定义某类型数组,这里的arr数组后面只能push进number类型的数据,如果是其他类型,会报错的。
2.元组
这个概念与数组类似,但又比数组神奇——js中的数据可以存放任意类型的元素,而静态类型语言的数组只能存放某一固定类型的元素,元组在我看来就是它俩的综合:既可以在里面存储不同类型的元素,又必须是某几种固定类型的元素。
定义一个元组,可以这么写:
let user: [string, number] = ['Betty', 20]
右边的部分是不是很眼熟?看起来就是一个数组。我觉得元组就是带有限制的数组。上面写的这一行代码有这几种含义:1、":"后的"[]"里有两种类型,代表初始化元组的时候,前两个元素的类型,初始化的时候如果少了一个元素,或者多了一个元素,或者这两个元素的类型与这里定义的不同都会报错。2、虽然前两个类型写死了,但元组里不一定只有这两个元素。作为一个js数组,长度是任意的,你还可以在后面push更多的元素,但后面的元素类型仅限于你在类型这边定义的这几种。
3.联合类型
js是弱类型的语言,所以可以任意地赋值给变量,作为js的超集,ts虽然有类型的限制,但是也允许给变量赋值多种类型的值。可以在给变量设置类型的时候,设置为联合类型。
联合类型可以这么写:
// 联合类型——冒号后面跟多个类型,类型之间用竖线分割,表示赋值的时候可以赋这几种类型的。
let numberOrString: number | stringnumberOrString = 222numberOrString = 'adv'// 用这个联合类型的变量可以访问这两个类型的共有属性,即valueOf和toString,非共有属性会报错
numberOrString.toString
"|" 表示或者的意思,这里的类型是一个联合类型,表示这个变量的值是number或者string。之后可以给变量赋值这两种类型之一的值,其他类型的就会报错。比较无奈的是,如果是联合类型的变量,只能访问这两个类型都有的共有属性,否则会报错。
4.枚举
枚举很容易理解,可以说是值只能为某几个值之一,例如十二生肖,十二星座,周一到周日都可以作为一个枚举。枚举是这么定义的:
// 定义一个关于方向的枚举
enum Direction {Up,Down,Left,Right
}
使用enum关键字来定义枚举,在枚举名后的花括号里定义枚举的成员,用逗号隔开每个成员。这是最基础的用法。再来介绍几点:1、枚举里的成员其实是有值的,通过枚举名.成员名能获取到对应成员的值,通过枚举名[值]也可以得到对应的成员。赋值的操作是在定义枚举的时候,在成员名字后用"="赋值。很明显这里做了值和成员的双向绑定。2、你可能会问我,这里没有给成员赋值啊,那值拿来的呢?如果我没有赋值,那么枚举里的第一个成员(这里是Up)的值就是0,之后的每个成员的值递增。如果初始值不是0,可以给第一个成员赋值为某个其他的数字。3、成员的值当然不一定是数字,也可以是字符串。假如你给第一个成员赋值为字符串,后面的成员的值无法递增,所以必须给每个成员都手动赋值才行。枚举比较常见的用法就是给每个成员的值设置为字符串,表示成员的含义:
// 定义一个关于方向的枚举
enum Direction {// 可以给成员赋值为字符串,使它有含义,但必须所有成员都赋值,否则会报错Up = 'UP',Down = 'DOWN',Left = 'LEFT',Right = 'RIGHT'
}
枚举可以用在什么场景上呢?可以用在“已知值是某几个值之一,判断具体为哪个值,要进行相应操作”的场景。比如,获取下一步行动的方向,可以用枚举里的成员进行一一判断,如果获取到的值是某个成员的值,那么就做对应的行动。
// 假设得到的值是UP,我要在枚举里面找值为这个字符串的成员
var value = 'UP'
if (value === Direction.Up) {console.log('Go up');
}
假如你的枚举值都是常量,例如我这里的四个方向的字符串是固定的,那么可以在定义枚举的时候,在enum关键字前面加上const关键字定义常量枚举,这样的好处是可以让性能更好,因为本来定义枚举的时候,编译出来的js里面有对枚举值和成员进行双向绑定的代码,如Direction[Direction["Up"] = 100] = "Up";而使用了const,编译出来的js就少了这些代码,把枚举名.成员名的代码全部编译成了成员值,显然这样写,编译后的js代码少了很多。
5.字面量
在ts中,我弄错了类型后,(如let num: number = undefined; num = 'str'; //定义了一个number类型的变量num(null和undefined是任意类型的子类型,所以可以让初值为undefined),却给它赋值'str')看到的报错提示是这样的:“不能将类型“"str"”分配给类型“number”。”刚开始我还一头雾水,啊这,翻译错了吧,'str'并不是类型啊,是我赋的值啊,前一个“类型”应该是“值”才对吧。可是我看到老师那边没有翻译的提示就是“Type“,并没有翻译错误,为什么要把一个这样的值叫成“类型”呢?
后来学到字面量的时候,啊这,一个值真的可以表示一种类型啊!假如某变量的值只能为某值,那么就把这个变量的类型定为这个值。
// 字面量
// 可以让变量的类型为一个值,这样,这个变量的值就必须是这个值,否则会报错
const num: 1 = 1
虽然我这里用的是const,但是换成var和let,效果是一样的,无法给这个变量赋其他值了。
那把值作为类型的特性可以怎么使用呢?跟前面的“联合类型”合起来就有妙用啦。假如我设置某变量的类型并不是单个字面量,而是多个字面量类型组成的联合类型,那么我赋值的时候就有来自ts的提示了,安全又方便。如下面这样:
// 如果把这里的字符串值当成一个类型来看,那么这是一个联合类型
// 定义一个变量,类型为这个联合类型
let toWhere: 'Up' | 'Down' | 'Left' | 'Right' = 'Left'
6.类型的别名
最后介绍一个简单又有趣的东西,给类型起别名。我刚才定义了一个很长的类型“'Up' | 'Down' | 'Left' | 'Right'”,这是一个由4个字面量组成的联合类型,假设我需要定义一个由10个甚至n个字面量组成的联合类型,那写起来得多长啊。怎样让它变得简短又好记呢?可以使用type关键字为类型起别名。
如下面这样:(type 别名 = 类型)
// 现在为联合类型起别名
type Directions = 'Up' | 'Down' | 'Left' | 'Right'// 定义一个变量,类型为这里的联合类型的别名
let toWhere: Directions = 'Left'
这里的别名可以很简单而且有含义,之后使用这种类型就方便多了,使用别名即可。
好了,今天先介绍到这里,本来还想再说点别的,发现现在字数够多了,下次想写关于类型推断、类型守卫和泛型的内容,写在下一篇吧。