【TypeScript】常用类型声明详情概述
目录
TypeScript常用类型
类型注解
TS类型概述
原始类型
数组类型
对象类型
函数类型
类型别名
接口
元组
字面量类型
枚举
any类型
typeof操作符
类型推论
类型断言
TypeScript常用类型
TypeScript是JS的超集,TS提供了JS的所有功能,并额外增加了类型系统。
JS有类型,但是JS不会检查变量的类型是否发生变化,而TS会检查。TypeScript类型系统的主要优势:可以显示标记出代码的意外行为,从而降低了发生错误的可能性。
类型注解
类型注解就是为了给代码添加类型约束,如下代码中 : number 就是类型注解。
let age: number = 12
上述代码中,约定变量age的类型为number(数值类型),即约定了什么类型就只能给变量赋值该类型的值,否则就会报错。如下所示:
TS类型概述
在TS中常用的基础类型细分为两类:一类是JS已有类型,另一类是TS新增类型。
JS已有类型
1)原始类型:number/string/boolean/null/undefined/symbol。
2)对象类型:object(包括:数组、对象、函数等)。
TS新增类型
联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any等。
原始类型
原始类型:number/string/boolean/null/undefined/symbol。特点:简单,这些类型完全是按照JS中的类型名称来书写的。
原始类型中也就 symbol 是ES6引入了一种新的原始数据类型,不熟悉的可以看一下我之前写的文章:Symbol文章
let age: number = 12
let myname: string = '张三'
let isloding: boolean = true
let a: null = null
let b: undefined = undefined
let s: symbol = Symbol()
数组类型
数组类型的两种写法:(推荐使用number[]写法)
let numbers: number[] = [1,2,3,4,5]
let string: Array<string> = ['1','2','3','4','5']
当我们要求数组中既有 number 类型,又有 string 类型,可以通过以下方式书写:
let arr: (number | string)[] = [1,'2',3,'4',5]
竖线 “|” 在TS中叫做联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)。
对象类型
JS中的对象是由属性和方法构成,而TS中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。其写法如下:
let person: { name: string; age: number; say(): void; doing(Do: string): void} = {
name:'张三',
age: 18,
say(){},
doing(Do){}
}
如果一行代码只指定一个属性类型,可以换行来分隔多个属性类型,这样可以去掉分号 ;,如下
let person: { name: string
age: number
say(): void
doing(Do: string): void
} = {
name:'张三',
age: 18,
say(){},
doing(Do){}
}
方法的类型也可以使用箭头函数形式,如下:
对象的属性或方法也是可选的,此时就用到可选属性。可选属性的语法与函数可选参数的语法一致,都是用 ?(问号) 来表示,案例如下:
function myaxios(config: {url: string, method?: string}){}
myaxios({
url:''
})
函数类型
函数类型实际上指的是:函数参数和返回值的类型。为函数指定类型有以下两种方式:
单独指定参数返回值类型:
function add1(n: number,m: number):number{
return n+m
}
const add2 = (n: number,m: number): number =>{
return n+m
}
同时指定参数返回值类型:
// 当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。
// 注意:这种形式只适用于函数表达式。
const add2: (n: number,m: number) => number = (n,m)=>{
return n+m
}
void:如果函数没有返回值,那么函数返回值的类型为:void。如下:
function say(word: string): void{
console.log('hello',word);
}
say('world')
可选参数:使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数。如下:
// 可选参数:在可传可不传的参数名称后面添加 ? (问号)
function myslice(start: number,end?: number): void{
console.log('必选参数:',start,'可选参数:',end);
}
myslice(1)
myslice(1,2)
类型别名
类型别名(自定义类型):为任意类型起别名。当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用。如下:
// 使用 type 关键字来创建类型别名,类型别名可以是任意合法的变量名称
type unnumber = (number | string)[]
// 创建类型别名,直接使用该类型别名作为变量的类型注解即可。
let arr1: unnumber = [1,'2','s','3',12]
let arr2: unnumber = ['y',2,'v','3',2]
接口
当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。
// 使用 interface 关键字声明接口
// 因为每一行只有一个属性类型,因此属性类型后面可以没有 分号 ;
interface myperson {
name: string
age: number
say(): void
}
// 接口声明后,直接使用接口名称作为变量的类型
let person: myperson = {
name:"张三",
age:18,
say(){}
}
let person1: myperson = {
name:"李四",
age:22,
say(){}
}
接口继承:如果两个接口之间有相同的属性或方法,可以将公共的属性和方法抽离出来,通过继承来实现复用。案例如下:
interface Tom {
name:string
age: number
sex:string
}
// 使用 extends (继承)关键字实现接口 Jack 继承 Tom,此时Jack就有了Tom所有属性和方法
interface Jack extends Tom { hobby: string}
type(类型别名)与 interface(接口)的对比:
相同点:都可以给对象指定类型。
不同点:接口只能为对象指定类型;类型别名不仅可以为对象指定类型,实际上可以为任意类型指定别名。
元组
元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引值所对应的类型。
// 元组中有两个元素且都为number类型,所以数组元素只能是两个number类型的元素
let position: [number,number] = [122.33,33.333]
字面量类型
字面量类型就是把某个具体的特定的类型作为TS的类型,如下:
此处的 'hello' 就是一个字面量类型,除字符串外任何JS字面量(对象、数组等)都可作为类型使用。
字面量类型一般配合联合类型一起使用,比如在游戏中,游戏的方向只能是上下左右四种情况任意一个,用特定的字符串来表示上下左右比直接使用 string 类型更加精确严谨。如下:
function changeDirection(directive: 'up' | 'down' | 'left' | 'right'){}
changeDirection('left')
枚举
枚举功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。枚举:定义一组命名常量,它描述一个值,该值可以是这些命名常量中的一个。
// 使用 enum 关键字定义枚举
enum Direction {
Up,Down,Left,Right
}
function changeDirection(direction: Direction){}
// 类似于JS中的对象,直接通过 .语法访问枚举成员。
changeDirection(Direction.Up)
注意:枚举成员是有值的,默认是从 0 开始自增的数值,当然我们也可以为枚举成员初始化值。
字符串枚举:枚举成员的值为字符串。
注意:字符串枚举没有自增长行为,因此字符串枚举的每个成员必须都有初始值。
总结:
枚举是TS为数不多的非JS类型扩展的特性之一,因为其他类型仅仅被当作类型,而枚举不仅用作类型,还提供值(枚举成员都是有值的)。也就是说其他类型会在编译JS代码时自动移除而枚举类型会被编译成JS代码!如下:
枚举与前面讲到的字面量+联合类型组合的功能类似,都是用来表示一组明确的可选值列表。
一般情况下推荐字面量类型+联合类型的方式,相对枚举来说,这种方式更加直观简洁。
any类型
当值为any类型时,可以对该值进行任意操作,并且不会有代码提示。这样就是失去了TS类型的保护优势,所以不推荐使用。除非临时使用 any 来避免书写很长很复杂的类型!
let a: any = 122
a = '212'//没有报错
a(12)//没有报错
以下具有any类型的隐式情况:
typeof操作符
众所周知,JS中提供了typeof操作符,用来在JS中获取数据的类型。
console.log(typoef 'hello');//打印 string
TS也提供了typeof操作符,可以在类型上下文中引用变量或属性的类型(类型查询),根据已有变量的值,获取该值的类型,来简化类型的书写。
注意:typeof只能用来查询变量或属性的值,无法查询其他形式的类型,比如函数调用类型
总结:
number:任意数字、string:任意字符串、boolean:布尔值、字面量:其本身、any:任意类型、unknown:类型安全的any、void:undefined、never:不能是任何值、object:任意的JS对象、array:任意JS数组、tuple:TS新增类型,固定长度数组、enum:枚举
类型推论
在TS中,某些没有明确指出类型的地方,TS的类型推论机制会帮助提供类型。换句话说:由于类型推论的存在,某些地方的类型注解可以省略不写!而发生类型推论有两种常见场景:1、声明变量并初始化值 2、决定变量返回值时。推荐:能省略类型注解的地方就省略,如果不知道类型,可以通过鼠标放在变量名称上,利用VScode的提示来查看类型。
类型断言
在日常开发中,有时你会比TS更加明确一个值的类型,此时可以使用类型断言来指定更具体的类型。比如我们要如何拿到a标签的href属性呢?
我们知道getElementById方法返回值的类型是HTMLElement,该类型只包含所有标签的公共属性或方法,不好含a标签特有的href属性,因此这个类型太宽泛,无法操作某些标签特有的属性或方法,这个时候就需要用到类型断言了。
使用类型断言,如下:
通过类型断言,a的类型变的更具体,这样就可以访问a标签特有的属性或方法了。
// 使用as关键字实现类型断言
// as关键字后面的类型是一个更具体的类型 HTMLAnchorElement 是 HTMLElement的子类型
const a = document.getElementById('a') as HTMLAnchorElement
a.href
另一种语法,使用 <> 语法(这种语法不常用,了解即可),如下:
// 使用as关键字实现类型断言
// as关键字后面的类型是一个更具体的类型 HTMLAnchorElement 是 HTMLElement的子类型
const a = <HTMLAnchorElement>document.getElementById('a')
a.href