TypeScript入坑指南(三)

3,255 阅读5分钟

上一篇文章简单讲了tsjs相同的数据类型和voidenumany等类型,这一篇将会继续讲到其它类型。

类型推论

我们在前面有说过,当我们未给某个变量声明类型时,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,既然作为Personme,肯定要有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
}

值得注意的是,如果设置了任意属性值的类型不为anystring,则接口中确定属性可选属性值都应为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)

结语

本篇讲了接口、数组、函数等类型,篇幅比较长,总结得也不够细腻,后续还会对本文不断优化补充。