基础类型

基础类型包括Stringnumberboolean

1
2
3
4
// 通过冒号标注变量值的类型
let title: string = '小康'
let age: number = 18
let isOk: boolean = true

空和未定义类型

1
2
3
4
5
6
7
8
let a: null
let b: undefined

// a与b无法被赋值,但是可以将其赋值给其他类型

let c: string = '1'
c = b
console.log(c)

如果声明了一个变量,但没有赋值,那么值默认为undefined,类型为any

避免赋值为空值时的配置

修改tsc配置文件,加入字段strictNullChecks

1
2
3
4
5
6
7
8
9
{
"compilerOptions": {
"outDir": "./dist",
"target": "ES2015",
"watch": true,
"strictNullChecks": true
},
"include": ["./src/**/*"]
}

此时就不能将空值赋值给其他变量

image-20201214091524329

对象类型

内置对象

1
2
3
4
5
6
7
8
9
10
11
12
// 数组
let arr: Array<number> = [1, 2, 3]
// 时间
let d1: Date = new Date()
// 对象
let user: {
username: string
age: number
} = {
username: 'aaa',
age: 23
}

自定义对象类型

自定义对象相较于内置对象的优点是复用性高。但其不能作为值,interface只存在于编译前,编译后是不存在的。

1
2
3
4
5
6
7
8
interface Person {
username: string
age: number
}
let user: Person = {
username: '小康',
age: 18
}

也可以通过对象定义

1
2
3
4
5
class Person {
constructor(public username: string, public age: number) {}
}

let user: Person = new Person('小康', 18)

通过对象定义可以作为值使用。因此当只作为某个函数接收的参数结构,可以使用interface

1
2
3
4
5
6
7
8
9
interface Person {
username: string
age: number
}
function login(options: Person) {}
login({
username: '小康',
age: 18
})

包装类型

1
2
3
4
5
6
7
8
9
let str1: string
str1 = '1'
// 不可以
str2 = new String('1')

let str2: String
// 可以
str2 = '1'
str2 = new String('1')

数组类型

Typescript中数组存储的类型必须一致,所以在标注数组类型的时候,同时要标注数组中存储的数据类型

1
2
3
4
5
6
// 泛型标注
let arr1: Array<number> = [1, 2, 3]
arr1.push(4)
// 简单标注
let arr2: String[] = ['a', 'b', 'c']
arr2.push('d')

元组类型

元组类型与数组类似,但可以存储不同类型。

1
2
3
4
5
// 元组类型中初始化时,值与类型必须一一匹配
let data1: [string, number] = ['小康', 18]
// 但是元组类型可以添加值,但只能添加定义中的类型
data1.push(1)
data1.push('小康1')

枚举类型

数字枚举

枚举的作用组织收集一组关联数据的方式,通过枚举我们可以给一组有关联意义的数据賦予一些友好的名字

1
2
3
4
5
6
7
8
9
// 枚举类型一旦定义无法在被赋值
enum HTTP_CODE {
OK = 200,
NOT_FOUNT = 404,
// 如果没有指定值,那么默认值为上一个值 + 1
METHOD_NOT_ALLOWED
}

HTTP_CODE.OK

注意事项:

  • key 不能是数字
  • value 可以是数字,称为 数字类型枚举,也可以是字符串,称为 字符串类型枚举,但不能是其它值,默认为数字:0
  • 枚举值可以省略,如果省略,则:
    • 第一个枚举值默认为:0
    • 非第一个枚举值为上一个数字枚举值 + 1
  • 枚举值为只读(常量),初始化后不可修改

字符串类型枚举

枚举类型的值,也可以是字符串类型

1
2
3
4
5
6
enum URLS  {
USER_REGISETER = '/user/register',
USER_LOGIN = '/user/login',
// 如果前一个枚举值类型为字符串,则后续枚举项必须手动赋值
INDEX = 0
}

注意:如果前一个枚举值类型为字符串,则后续枚举项必须手动赋值

小技巧:枚举名称可以是大写,也可以是小写,推荐使用全大写(通常使用全大写的命名方式来标注值为常量)

无值类型

表示没有任何数据的类型,通常用于标注无返回值函数的返回值类型,函数默认标注类型为:void

1
2
3
function fn():void {
// 没有 return 或者 return undefined
}

strictNullChecksfalse 的情况下,undefinednull 都可以赋值给 void ,但是当 strictNullCheckstrue 的情况下,只有 undefined 才可以赋值给 void

Never类型

当一个函数永远不可能执行 return 的时候,返回的就是 never ,与 void 不同,void 是执行了 return, 只是没有值,never 是不会执行 return,比如抛出错误,导致函数终止执行

1
2
3
function fn(): never {
throw new Error('error')
}

任意类型

1
2
3
4
let any1: any
any1 = 1
any1 = '1'

有的时候,我们并不确定这个值到底是什么类型或者不需要对该值进行类型检测,就可以标注为 any 类型

1
let a: any;
  • 一个变量申明未赋值且未标注类型的情况下,默认为 any 类型
  • 任何类型值都可以赋值给 any 类型
  • any 类型也可以赋值给任意类型
  • any 类型有任意属性和方法

注意:标注为 any 类型,也意味着放弃对该值的类型检测,同时放弃 IDE 的智能提示

小技巧:当指定 noImplicitAny 配置为 true,当函数参数出现隐含的 any 类型时报错

未知类型

unknow,3.0 版本中新增,属于安全版的 any,但是与 any 不同的是:

  • unknow 仅能赋值给 unknowany
  • unknow 没有任何属性和方法

函数类型

JavaScript 函数是非常重要的,在 TypeScript 也是如此。同样的,函数也有自己的类型标注格式

  • 参数
  • 返回值
1
函数名称( 参数1: 类型, 参数2: 类型... ): 返回值类型;
1
2
3
function add(x: number, y: number): number {
return x + y;
}

类型保护

我们通常在JavaScript中通过判断来处理⼀些逻辑,在TypeScript中这种条件语句块还有另外⼀个特性:根据判断逻辑的结果,缩⼩类型范围(有点类似断⾔),这种特性称为 类型保护,触发条件:

  • 逻辑条件语句块:if、else、elseif
  • 特定的⼀些关键字:typeof、instanceof、in……

typeof

1
2
3
4
5
6
7
8
9
function fn(a: string | number) {
if (typeof a === 'string') {
// 此时 a 变量就是 string类型
a.substring(1)
} else {
// 这里就是当变量a为number类型时
a.toFixed()
}
}

instanceof

1
2
3
4
5
6
7
function fn(a: Date|Array<any>) {
if (a instanceof Array) {
a.push(1);
} else {
a.getFullYear();
}
}

in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface IA {
x: string;
y: string;
}
interface IB {
a: string;
b: string;
}
function fn(arg: IA | IB) {
if ('x' in arg) {
// ok
arg.x;
// error
arg.a;
} else {
// ok
arg.a;
// error
arg.x;
}
}

字面量类型保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface IA {
type: 'IA';
x: string;
y: string;
}
interface IB {
type: 'IB';
a: string;
b: string;
}
function fn(arg: IA | IB) {
if (arg.type === 'IA') {
// ok
arg.x;
// error
arg.a;
} else {
// ok
arg.a;
// error
arg.x;
}
}

自定义类型保护

1
2
3
4
5
6
7
8
9
10
11
12
function canEach(data: any): data is Element[]|NodeList {
return data.forEach !== undefined;
}
function fn2(elements: Element[]|NodeList|Element) {
if ( canEach(elements) ) {
elements.forEach((el: Element)=>{
el.classList.add('box');
});
} else {
elements.classList.add('box');
}
}

data is Element[]|NodeList 是⼀种类型谓词,格式为: xx is XX ,返回这种类型的函数就可以被TypeScript识别为类型保护

类型操作

typeof

在 TypeScript 中, typeof有两种作⽤

  • 获取数据的类型
  • 捕获数据的类型
1
2
3
4
5
6
7
let str1 = 'xiaokang.me'
// 如果是 let ,把 'string' 作为值
let t = typeof str1
// 如果是 type,把 'string' 作为类型
type myType = typeof str1
let str2: myType = '小康'
let str3: typeof str1 = '小康'

keyof

1
2
3
4
5
6
7
8
9
let p1 = {
name: 'xiaokang',
age: 18
}
// type PT = typeof p1

function getPersonVal(k: keyof typeof p1) {
return p1[k]
}

in

1
2
3
4
5
6
7
8
9
10
interface Personal {
name: string
age: number
}

type personKeys = keyof Personal

type newPersonal = {
[k in personKeys]: string
}

in 后⾯的类型值必须是string或者number或者symbol

类型兼容

TypeScript的类型系统是基于结构⼦类型的,它与名义类型(如:java)不同(名义类型的数据类型兼容性或等价性是通过明确的声明或类型的名称来决定的)。这种基于结构⼦类型的类型系统是基于组 成结构的,只要具有相同类型的成员,则两种类型即为兼容的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
name: string;
age: number;
}
class Cat {
name: string;
age: number;
}
function fn(p: Person) {
p.name;
}
let xiaohua = new Cat();
// ok,因为 Cat 类型的结构与 Person 类型的结构相似,所以它们是兼容的
fn(xiaohua);