上一篇文章简单讲了ts跟js相同的数据类型和void、enum和any等类型,这一篇将会继续讲到其它类型。
类型推论
我们在前面有说过,当我们未给某个变量声明类型时,ts会根据上下文,自动推导出变量的类型,这种过程其实就是类型推论。 看下面的例子:
let firstStr = 'hello ts'
firstStr = 123
对于js来说,这是没有问题的,但是在ts里,它相当于以下内容
let firstStr: string = 'hello ts'
firstStr = 123
因此就会编译报错
error: Type 'number' is not assignable to type 'string'
如果定义变量时,既未赋值,又未声明定义,那这时就相当于any类型,不管之后怎样赋值,它都是被当成any类型而不被检查。
联合类型
联合,顾名思义,多个类型之间存在着某种py交易,让变量可以在多种类型里面取值。像上面的例子,既想让firstStr可以作为字符串,又可以作为数值,那可以像这样让它们用|分隔开,而像没有py的类型如boolean,不好意思,不是好基友,不允许你成为这种类型:
let firstStr: string | number
firstStr = 'hello ts'
firstStr = 123
// error
firstStr = true
当然,好基友之间也并非百分百无私的,当变量类型不确定时,我们只能访问联合类型的共有属性和共有方法:
// string和number共有toString方法
function union (value: string | number) {
return value.toString()
}
// error number没有split方法
function union (value: string | number) {
return value.split('')
}
同样,当变量被赋值时,会根据类型推论推导出变量的类型:
let firstStr: string | number
firstStr = '123'
firstStr.split('') // ['1', '2', '3']
firstStr = 5
firstStr.split() // error number类型没有split方法
接口(Interfaces)
接口在Ts中是一个很灵活的概念,Ts的核心原则之一就是对值所具有的类型进行检查,而接口的作用就是对类型进行命名,对对象的结构进行“描述”,对代码进行契约定义。
interface Person {
name: string;
age: number;
sex: string;
}
let me: Person = {
name: 'Ysom',
age: 24,
sex: 'male'
}
首先创建一个接口Person,并创建一个类型为Person的变量me,既然作为Person的me,肯定要有name,有age还有sex,但是如果me多了个翅膀wing,或者少了sex,那肯定是不行的
// error
let me: Person = {
name: 'Ysom',
age: 24,
sex: 'male',
extra: 'wing'
}
// error
let me: Person = {
name: 'Ysom',
age: 24
}
可选属性
有时候,我们并不想暴露我们全部的属性,比如女生的年龄、体重,那么就可以将年龄属性设置为可选属性
interface Person {
sex: string;
age?: number;
}
// 男的 年龄无所谓 暴露吧
let male: Person = {
sex: 'female',
age: 18
}
// 女生 年龄是隐私 保密
let feMale: Person = {
sex: 'male'
}
只读属性
一些对象属性只允许对象创建时赋值,比如我们的身份证id,可以用readonly处理
interface Person {
readonly idCard: string;
name: string;
}
let me: Person = {
idCard = '441239XXXXXX11',
name: 'Ysom'
}
// error
me.idCard = '7463234XXXX1111'
任意属性
有时候我们希望接口可以接受任意的属性,就像我们有着一个超人梦,可以给自己定义很多额外的属性,比如会飞,刀枪不入,变身等等。那我们就可以使用 [propName: string]: any 定义任意属性名取string类型值,属性值取any类型值。
interface Person {
name: string;
age: number;
[propName: string]: any;
}
let me: Person = {
name: 'superman',
age: 24,
wing: 'two',
fly: true
}
值得注意的是,如果设置了任意属性值的类型不为any如string,则接口中确定属性和可选属性值都应为string类型
interface Person {
name: string,
age?: number,
[propName: string]: string
}
// error age也应为string类型
let me: Person = {
name: 'superman',
age: 24,
wing: 'two'
}
数组类型
用类型+方括号表示
// 表示数组元素全都为number
let arr: number[] = [1,2,3,4,5]
// error
let arr: number[] = [1,'1',2,'2']
用any表示,允许数组元素为不同的类型
let arr: any[] = [1,'1','2',2]
用接口表示
// 索引为number时,值也为number
interface InterArray {
[index: number]: number
}
let arr: InterArray = [1,2,3,4,5]
函数类型
在JS中,函数定义有两种方式:声明式和表达式
// 声明式
function sum (a,b) {
return a + b
}
// 表达式
let sum = function (a,b) {
return a + b
}
输入输出约束
在Ts中,需要对函数的输入(参数)和输出(返回)进行约束
function sum (a: number, b: number): number {
return a + b
}
而当函数没有返回时,就可用到上篇文章讲到的void类型
function logSum (a: number, b: number): void {
console.log(a + b)
}
参数约束
Ts对函数除了输入输出的类型进行约束之外,还对参数的个数有约束,数量不可多也不可少
function sum (a: number, b: number): number {
return a + b
}
// error
sum(a,b,c)
sum(a)
可选参数
参数个数受到限制,那还能设置可选参数吗?完全ok,类似于接口的写法,但是需要注意,可选参数要放在必需参数的后面
function isDog (a: string, b?:string) :string {
if (b) {
return `${a} is not a single dog`
}
return `${a} is a single dog`
}
isDog('王小虎', '胖妞')
默认参数
Ts中也可以对函数参数进行默认值设置,这时参数会被识别为可选参数,且不受可选参数必须在必需参数的后面的限制
function isDog (a: string='王小虎', b: string): string {
return `${a} and ${b} is a couple`
}
isDog('王小虎', '胖妞')
isDog(undefined, '胖妞')
剩余参数
在ES6中,可以使用...rest来获取函数的剩余参数,而在Ts中,也可以使用数组类型来定义rest参数
function logNum (arr: any[], ...items: any[]): void {
items.forEach(item => console.log(item))
}
logNum([], 1,2,3,4,5)
结语
本篇讲了接口、数组、函数等类型,篇幅比较长,总结得也不够细腻,后续还会对本文不断优化补充。