外观
基础
类型注解
TypeScript比JavaScript多出的就是类型限制,它将JavaScript变成了一款强类型语言。
TypeScript为变量添加类型限制是通过类型注解实现的,类型注解的语法是let/const 变量名 : 类型。
let age: number = 18为变量添加类型后,为它赋值一个字符串类型的常量就会报错。
let age: number = "18"TypeScript的数据类型
TS中的常用基础类型分为两类:
- JS 已有类型:原始类型,简单类型( number/string/boolean/null/undefined ),复杂数据类型(数组,对象,函数等)。
- TS 新增类型:联合类型,自定义类型(类型别名),接口,元组,字面量类型,枚举,void等。
原始数据类型的用法和JavaScript的用法一致。
let age: number = 18
let myName: string = '老师'
let isLoading: boolean = false新增类型将在下面逐一介绍。
数组
TS数组的声明有两种语法。
// 写法一:
let numbers: number[] = [1, 3, 5]
// 写法二:
let strings: Array<string> = ['a', 'b', 'c']联合类型
联合类型表示一个元素可以有多种类型,对于数组,则可以允许数组包含不同元素,如同时包含字符串和数字。
TS中,使用“|”表示联合类型。|(竖线)在 TS 中叫做联合类型,即:由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种。
let timer: number | null = null
let arr: (number | string)[] = [1, 'a', 3, 'b']类型别名
若类型的种类过于复杂,我们可以自定义一个类型。创建类型别名后,直接使用该类型别名作为变量的类型注解即可。
TS使用type关键字自定义类型,语法是type 自定义类型名 = 其他类型。
type CustomArray = (number | string)[]
let arr : CustomArray = [1, "b", 3, "d"]这里注意,自定义类型名也是变量名称,要遵守定义变量名称的规则,如不能是关键字。
函数
参数和返回值类型
函数的类型实际上指的是:函数参数和返回值的类型。为函数指定类型的两种方式:单独指定参数或返回值的类型;同时指定参数、返回值的类型。
- 单独指定参数或返回值的类型:
// 函数声明
function add(num1: number, num2: number): number {
return num1 + num2
}
// 箭头函数
const add = (num1: number, num2: number): number => {
return num1 + num2
}- 同时指定参数、返回值的类型:
type AddFn = (num1: number, num2: number) => number
const add: AddFn = (num1, num2) => {
return num1 + num2
}当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。
这种形式只适用于函数表达式。
封装函数参数类型
若多个函数的参数和返回值类型相同,则可以将它封装成一个自定义类型。自定义类型只能用于赋值表达式,不能用于函数声明。
type myFuncType = (a: number, b: number) => number
// 这种函数不能用自定义函数类型
function add1(a: number, b: number): number {
return a + b
}
const add2: myFuncType = function(c, d) {
return c + d
}
const add3: myFuncType = (c, d) => c + d不同形式的函数
普通函数、匿名函数和箭头函数定义参数和返回值的位置如下例。
function add(param1: number, param2: number): number {
return param1 + param2
}
const myPrint = function (myArguments: string[]): void {
console.log(...myArguments)
}
const myFunc = (myName: string, myAge: number): void => {
console.log(`${myName}今天${myAge}岁了`)
}在TS中,箭头函数的括号不能省略。
void类型
如果函数没有返回值,那么,函数返回值类型为:void。如果一个函数没有返回值,在TS的类型中,应该使用void类型,而不是undefined。
function greet(name: string): void {
console.log('Hello', name)
}如果什么都不写,函数的返回值类型为:void。
const add = () => {}这种写法是明确指定函数返回值类型为void,与上面不指定返回值类型相同。
const add = (): void => {}如果指定返回值类型为undefined,函数体中必须return undefined。
const add = (): undefined => {
return undefined
}可变参数
使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。
比如数组的 slice 方法,可以 slice() 也可以 slice(1) 还可以 slice(1, 3)。它的定义如下。
function mySlice(start?: number, end?: number): void {
console.log('起始索引:', start, '结束索引:', end)
}
mySlice(1, ); // start为1,end为undefined。定义可选参数的语法就是在参数后面加一个问号"?"。
注意,可选参数必须在必选参数的后面,如下面的语法是错误的。
function mySlice(start?: number, end: number): void {
console.log('起始索引:', start, '结束索引:', end)
}对象类型
JS中的对象是由属性和方法构成的,而TS对象的类型就是在描述对象的结构(有什么类型的属性和方法)。
几种对象类型的写法如下。
// 空对象
let person: {} = {}
// 有属性的对象
let person: { name: string } = {
name: '同学'
}
// 既有属性又有方法的对象
// 在一行代码中指定对象的多个属性类型时,使用 `;`(分号)来分隔
let person: { name: string; sayHi(): void } = {
name: 'jack',
sayHi() {
}
}
// 对象中如果有多个类型,可以换行写:
// 通过换行来分隔多个属性类型,可以去掉 `;`
let person: {
name: string
sayHi(): void
} = {
name: 'jack',
sayHi() {
}
}使用对象类型在于:
- 使用 {} 来描述对象结构
- 属性采用 属性名: 类型的形式
- 方法采用 方法名(): 返回值类型的形式
封装对象类型
我们也可以将对象类型封装成自定义类型。
type Person = {
name: string,
age: number
}
let person: Person = {
name: "张三",
age: 19
}对象的属性有多种多样,包括普通成员变量、成员函数等,以及可选变量,它们的定义语法如下。
type MyObject = {
name: string,
age: number,
sayHi: () => void, //成员函数类型
gender?: string //可选属性
}
let myObj:MyObject = {
name: "张三",
age: 18,
sayHi: function () {
console.log(`${this.name}的年龄为${this.age}`)
},
gender: "男"
}
console.log(myObj)
myObj.sayHi()使用对象的可选属性
在声明对象的类型时,使用了英文问号代表此属性可选。在大型项目中,我们可能无法确定一个对象是否有此属性,往往需要加一个if判断。在TS中我们可以用以下几种方法尝试使用对象可能存在的属性。
// 通过if判断
if (obj.val) {
console.log(obj.val)
}
// 通过逻辑与
obj.val && console.log(obj.val)
// 通过?符号,它只能在链式调用的中间使用
obj.someField?.func() //相当于obj.someField && obj.someField.func()接口
接口实际上比type类型别名更早出现,接口是用来约束对象的属性的,不过现在使用type更多。
使用interface关键字定义接口,其他语法和type运算符基本一致。
interface Person {
name: string
age: number
gender?: string
sayHi: () => void
}
let person: Person = {
name: "张三",
age: 18,
sayHi: function () {
console.log(`你好,我是${this.name},我今年${this.age}岁`)
}
}
person.sayHi()接口继承
若两个对象有相同的属性,而一个对象比另一个对象多一些属性,那么前者就可以继承自后者,将公共属性提取出来做成父接口,然后每一个子接口在继承自父接口的同时,可以拥有自己的属性。
TypeScript使用extends关键字继承接口。继承后,父接口中有的属性,子接口就不需要重复定义了。
interface Person {
name: string
age: number
}
interface Student extends Person {
class: string,
score: number
}
let student : Student = {
name: "张三",
age: 18,
class: "三年一班",
score: 100
}元组类型
元组类型可以理解为不能改变的列表类型。元组的定义为[元素类型1, 元素类型2, ...],中括号不变,元素类型可以有多种。在实例化元组时,元素的类型和顺序必须和元组定义的一致。
type Position = [number, number]
let point = [1, 2]类型推断
若在声明变量时没有明确指定变量类型时,若为元素赋值,则TS会自动推断元素为什么类型,然后隐式地限制它的类型。
// 变量 age 的类型被自动推断为:number
let age = 18
// 函数返回值的类型被自动推断为:number
function add(num1: number, num2: number): number {
return num1 + num2
}在日常开发中,我们可以选择省略类型注解,充分利用TS类型推论的能力,提升开发效率。如果不知道类型,可以通过鼠标放在变量名称上,利用 VSCode 的提示来查看类型。
字面量类型
下面的代码,两个变量的类型不一致。
let str1 = 'Hello TS'
const str2 = 'Hello TS通过 TS 类型推论机制,可以得到答案:
- 变量 str1 的类型为:string
- 变量 str2 的类型为:'Hello TS'
原因:
- str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string。
- str2 是一个常量(const),它的值不能变化只能是 'Hello TS',所以,它的类型为:'Hello TS'。
注意:此处的 'Hello TS',就是一个字面量类型,也就是说某个特定的字符串也可以作为 TS 中的类型。
任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用。
字面量: { name: 'jack' } [] 18 20 'abc' false function() {}
枚举类型
枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。
TypeScript使用enum关键字定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。
// 创建枚举
enum Direction { Up, Down, Left, Right }
// 使用枚举类型
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Up)- 使用 enum 关键字定义枚举
- 约定枚举名称以大写字母开头
- 枚举中的多个值之间通过 , (逗号)分隔
- 定义好枚举后,直接使用枚举名称作为类型注解
枚举的实现
枚举内部是通过数字实现的。从第一个元素开始,每个元素的值依次是0、1、2,依次类推。我们也可以将其显式地赋一个值,以满足我们的更多需求。
enum Direction {
Up = "Up",
Down = "Down",
Left = "Left",
Right = "Right"
}
console.log(Direction.Up)上述TS代码编译后的JS代码如下。
var Direction;
(function (Direction) {
Direction["Up"] = "Up";
Direction["Down"] = "Down";
Direction["Left"] = "Left";
Direction["Right"] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up);any类型
any类型代表这个变量可以为任何类型。我们不推荐使用any类型,因为这样会失去TS为变量的类型做保护的机制。
若一个元素为any类型,我们可以对该值进行任何操作,并且没有错误提示。
let obj: any = {x: 0}
obj.bar = 100
obj() // 编译不会有错误 运行才有错误尽可能的避免使用 any 类型,除非临时使用 any 来“避免”书写很长、很复杂的类型。
其他隐式具有 any 类型的情况:
- 声明变量不提供类型也不提供默认值
- 函数参数不加类型
注意:因为不推荐使用 any,所以,这两种情况下都应该提供类型。
类型断言
有时候我们会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。
const aLink = document.getElementById('link')
console.log(aLink.href) // 这里会报错,因为返回的元素是HTMLElement,而不是a标签这时我们就需要显式地指定该方法的返回值类型,以便于使用TS原本识别不出的属性。
const aLink = document.getElementById('link') as HTMLAnchorElement
console.log(aLink.href) // 这里会报错,因为返回的元素是HTMLElement,而不是a标签