可以将 TS 中的常用基础类型细分为两类:
- JS 中已有类型:
- 原始类型:number、string、boolean、null、undefined、symbol
- 对象类型:object(数组、对象、函数等对象)
- TS 新增类型:
// TS 自动推断 name 为 string 类型 let name = ‘xinxianying’ name = ‘li’ // 不报错 name = 10 // 报错
<a name="BWRsP"></a>
## 原始类型(基础类型、简单类型)
特点:简单。这些类型完全按照 JS 中类型的名称来书写(小写)。
<a name="q6qYk"></a>
### String
```typescript
const say: string = 'hello'
Number
const num: number = 100
Boolean
const isDone: boolean = true
null
const n: null = null
undefined
const u: undefined = undefined
Symbol
const s: symbol = Symbol()
对象类型(复杂类型、引用类型)
特点:对象类型在 TS 中更加细化,每个具体的对象都有自己的类型语法。
Array 数组类型
数组类型的两种写法:(推荐使用 number[]
)
// 指定数组元素只能为 number 类型
const arr: number[] = [1, 2, 3]
// 第二种方式
const arr: Array<number> = [1, 2, 3]
联合类型
由两个或多个其他类型组成的类型称为 联合类型,表示其值可以是这些类型中任意一种。
语法:用 |(竖线)隔开不同的类型 const s: string | number = 1
注意:复杂类型声明联合类型时,必须用小括号()包起来。
// 复杂类型声明联合类型时,必须用小括号()包起来
const arr: (number | string)[] = [1, 'a', 2, 'c']
const arr: Arrat<number | string> = [1, 'a', 2, 'c']
/*
* 如果不是用小括号,那下面的类型注解则变成了:
* arr 是 number 类型,或者是 stirng 类型的数组
*/
const arr: number | string[] = 1
类型别名(自定义类型)
为任意类型起别名。
当同一种复杂的类型被多次使用时,可以通过类型别名,简化该类型的使用。
语法:
- 使用 type 关键字创建类型别名。
- 可以是任意合法的变量名称。
- 创建类型别名后,直接使用该类型别名作为变量的类型注解即可。 ```typescript type CustomArray = (number | string)[]
const arr: CustomArray = [1, ‘a’, 2, ‘b’]
<a name="uoepg"></a>
### 函数类型
指函数参数和返回值的类型。如果声明了函数返回值类型,那么该函数必须 **return **一个该类型的值。<br />函数类型声明方式有两种:
- 单独指定参数、返回值的类型
- 同时指定参数、返回值的类型
<a name="zImtC"></a>
#### 单独指定参数、返回值的类型
```typescript
// 方式一:函数声明
function getName(name: string): string {
return name
}
say(1) // 报错
say('xinxianying') // 不报错
// 方式二:函数表达式
const add = (num1: number, num1: number): number => {
return num1 + num2
}
同时指定参数、返回值的类型
这种形式只适用于函数表达式。const add: (num1: number, num2: number) => number = (num1 + num2) => {}
const add: (num1: number, num2: number) => number = (num1 + num2) => {
return num1 + num2
}
void 类型
如果函数没有返回值,那么该函数的返回值类型为: void
function log(text: string): void {
console.log(text)
}
可选参数
使用函数实现某个功能时,有些参数并不是必需要传,这种情况下,在给函数参数指定类型时,就可以使用可选参数。
语法:在非必传的参数名称后面添加 ?(问号)即可。
注意:可选参数只能出现在参数列表的最后。
function sayName(obj: {first: string, last?: string}){
// 因为 last 非必传,有可能为 undefined,所以调用 `toUpperCase` 可能会报错
console.log(obj.last.toUpperCase()) // 报错
console.log(obj.last?.toUpperCase()) // 不报错
}
sayName({ first: 'li' }) // 不报错
sayName({ first: 'li', last: 100 }) // 报错
sayName({ first: 'li', last: 'xiao' }) // 不报错
匿名函数
当一样函数出现在 TypeScript 可以确定它如何被调用的地方的时候,这个函数的参数会自动的指定类型。
const names = ['xin', 'wei', 'li']
names.forEach((name) => {
// 此时 TS 推断 name 类型为 `string`,所以调用 String 的方法不会报错
console.log(name.toUpperCase())
})
Object 对象类型
JS 中对象是由属性和方法构成的,而在 TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。
对象的属性或方法也可以是可选的,可选属性的语法和函数的可选参数语法一致,使用 ?(问号)来标识。
// 声明对象类型,可以用`,`或者';'分割
function move(pt: { x: number, y:number }){
}
const person: {
name: string,
age: number,
sayHi(text: string): void,
run: () => void,
eat?: boolean
} = {
name: '辛宪英',
age: 22,
sayHi('hello'){},
run(){}
}
interface 接口类型
当一个对象类型被多次使用时,一般会使用 接口(interface)来描述对象的类型,以达到复用的目的。
语法:
- 使用 interface 关键字声明接口
- 接口名称可以是任意合法的变量名称
- 接口声明后,直接使用接口名称作为变量的类型
- 因为每行只能有一个属性类型,因此属性类型后可以不写分号(;) ```typescript interface Person { name: string; age: number; sayHi(): void; eat?: boolean; }
const p1: Person = { name: ‘辛宪英’, age: 22, sayHi(){} }
<a name="runib"></a>
#### 接口继承
当两个接口之间出现相同的属性或方法时,可以将公共的属性或方法抽离出来,通过继承实现复用。<br />语法:
- 使用 **extends**(**继承**)关键字继承
- 继承后将拥有被继承的属性或方法
```typescript
interface Point2D {
x: number;
y: number;
}
// 重复属性
interface Point3D {
x: number;
y: number;
z: number;
}
// 使用继承的方式
interface Point3D extends Point2D {
z: number;
}
const p3: Point3D = { x: 1, y: 2, z: 3 }
interface(接口)与 type(类型别名)的对比
相同点:
- 都可以给对象指定类型
不同点:
- 接口只能为对象指定类型
- 接口可以继承
- 类型别名不仅可以为对象指定类型,还可以为任意类型指定别名
interface P {} type P = {} type S = string | number
Tuple 元组类型
元组(Tuple)类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型。// 长度为 2,且每项数据类型都为 number const position: [number, number] = [32.433759, 44.874275]
字面量类型
当需要控制参数的值为指定范围内的结果时,可以使用字面量类型。
TS 中,任意字符串和 JS 字面量(如:对象、数字等…)都可以作为 TS 中的类型: ```typescript let str1 = ‘hello’ // 类型推论为 string const str2 = ‘hello’ // 类型推论为 hello
let num: 18 = 18 // 类型指定为 18,且值也只能为 18 了
示例:在贪吃蛇游戏中,游戏的方向的可选值只有“上、下、左、右”中任意一个,相比于 **string** 类型,使用字面量更加精确、严谨。
```typescript
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {
}
enum 枚举类型
定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。
枚举功能类似于上面的字面量类型+联合类型组合功能,用以表示一组明确的可选值。
语法:
- 使用 enum 关键字定义枚举
- 约定枚举名称、枚举中的值以大写字母开头
- 枚举中的多个值至简用,(逗号)分隔
- 可直接使用枚举名作为类型注解
- 和对象一样,通过 .(点)语法访问枚举成员 ```typescript enum Direction { Up, Down, Left, Right }
function changeDirection(direction: Direction) { }
changeDirection(Direction.Up)
<a name="HeVvN"></a>
#### 枚举成员的值
枚举成员的默认值为从 0 开始自增的数值(类似数组的下标),当然也可以给枚举中的成员初始化值。
```typescript
enum Direction {
Up,
Down
}
console.log(Direction.Up) // 0
console.log(Direction.Down) // 1
数字枚举
枚举成员的值,为数字的枚举,我们称为:数字枚举。
当修改数字枚举某一个成员的值后,后面的成员根据当前修改的值自增。
// 后面的成员根据当前修改的值自增。
enum Direction {
Up = 10,
Down, // 11
Left, // 12
Right // 13
}
// 指定所有成员的值
enum Direction {
Up = 2,
Down = 4,
Left = 6,
Right = 8
}
字符串枚举
枚举成员的值,为字符串的枚举,我们成为:字符串枚举。
字符串枚举没有自增长行为,因此,字符串枚举的每个成员都必须有初始值。
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
枚举的特点及原理
枚举是 TS 为数不多的非 JavaScript 类型级扩展(不仅仅是类型)的特性之一。
枚举不同于其他类型仅仅被当做类型,枚举还提供值(枚举成员都是有值的)。
因此,其他类型会在编译为 JS 代码时自动移除,但枚举类型会被编译成 JS 代码。
// 编译前
enum Direction {
Up = 'UP',
Down = 'DOWN',
}
// 编译后
var Direction
(function (Direction){
Direction['Up'] = 'UP'
Direction['Down'] = 'DOWN'
})(Direction || (Direction = {}))
枚举与上面的字面量类型+联合类型组合的功能类似,都可以用来表示一组明确的可选列表。
any 任意类型
原则:不推荐使用 any,这会让 TypeScript 变成“AnyScript”(失去 TS 类型保护的优势)。
隐式具有 any 类型的情况:
- 声明变量不提供类型也不提供默认值时
- 函数参数不加类型时
当值的类型为 any 时,可以对该值进行任意操作,而不会有错误提示。
const obj: any = {
x: 0
}
// 以下静态校验都不会报错
// 访问不存在的属性或方法
obj.text
obj.foo()
// 当做函数调用
obj()
// 赋值其他类型的值
obj = 'hello'
// 赋值给其他类型的变量
const n: number = obj
类型推论
在 TS 中,某些没有明确指定类型的地方,TS 的类型推论机制会帮助提供类型。
以下几种情况可以省略类型注解:
- 声明变量并初始化时
const a = 1
- 决定函数返回值时
function show(text: string) { return text }
注意:如果声明变量但没有立即初始化值时,必须手动添加类型注解。const a: number
function sayName(obj: {first: string, last?: string}){
// 因为 last 非必传,有可能为 undefined,所以调用 `toUpperCase` 可能会报错
console.log(obj.last.toUpperCase()) // 报错
console.log(obj.last?.toUpperCase()) // 不报错
}
sayName({ first: 'li' })
类型断言
有时 TS 推论的类型太宽泛(不具体),这时可以使用 类型断言 来指定更具体的类型。
示例:
// 获取页面上的 a 标签,此时 TS 推论的类型为 HTMLElement
const aLink = document.getElementById('link')
// 但是 HTMLElement 只包含所有标签的公共属性,没有 a 标签特有的属性(如: herf)
console.log(aLink.herf) // undefined
// 使用类型断言,获取更具体的类型
const aLink = document.getElementById('link') as HTMLAnchorElement
// 或者
const aLink = <HTMLAnchorElement>document.getElementById('link')
console.log(aLink.herf) // ...
typeof
在 JS 中使用 typeof 操作符可以获取数据的类型:console.log(typeof 'hello') // string
在 TS 中,可以使用 typeof 在类型上下文中引用变量或属性的类型(类型查询)。
// JS
console.log(typeof 'hello') // string
// TS - 不使用 typeof
const p = { x: 1, y: 1 }
function onPoint(point: { x: number, y: number }){}
onPoint(p)
// TS - 使用 typeof
function onPoint(point: typeof p){}