TS专属数据类型&使用
# TS 专属数据类型&使用
# any类型的使用
// 当进行一些类型断言 as any
// 在不想给某些JavaScript添加具体的数据类型时(原生的JavaScript代码是一样)
let message: any = "Hello World"
message = 123
message = true
message = {
}
// message()
// message.split(" ")
console.log(message)
const arr: any[] = []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# unknown类型的使用
function foo() {
return "abc"
}
function bar() {
return 123
}
// unknown类型只能赋值给any和unknown类型
// any类型可以赋值给任意类型
let flag = true
let result: unknown // 最好不要使用any
if (flag) {
result = foo()
} else {
result = bar()
}
let message: string = result
let num: number = result
console.log(result)
export {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# void类型的使用
function sum(num1: number, num2: number) {
console.log(num1 + num2)
}
sum(20, 30)
// sum("abc", "cba")
1
2
3
4
5
6
2
3
4
5
6
# never类型的使用
never 表示永远不会发生值的类型,比如一个函数:
如果一个函数中是一个死循环或者抛出一个异常,那么这个函数会返回东西吗?不会,那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型;
// function foo(): never {
// // 死循环
// while(true) {
// }
// }
// function bar(): never {
// throw new Error()
// }
// 提前
// 封装一个核心函数
function handleMessage(message: string | number | boolean) {
switch (typeof message) {
case 'string':
console.log('string处理方式处理message')
break
case 'number':
console.log('number处理方式处理message')
break
// case 'boolean':
// console.log('boolean处理方式处理message')
// break
default:
const check: never = message // 仅仅作为编译时错误提示
}
}
handleMessage('abc')
handleMessage(123)
// 张三
handleMessage(true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# tuple—元组类型
可以存放不同类型的数组(类似)
tuple和数组有什么区别呢?
- 首先,数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中。(可以放在对象或者元组中)
- 其次,元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型
# 使用
// tuple元组: 多种元素的组合
// "why" 18 1.88
// 1.数组的弊端
// const info: any[] = ["why", 18, 1.88]
// const infoObj = {
// name: "why",
// age: 18,
// height: 1.88
// }
// const name = info[0]
// console.log(name.length)
// 2.元组的特点
const info: [string, number, number] = ["why", 18, 1.88]
const name = info[0]
console.log(name.length)
// const age = info[1]
// console.log(age.length)
export {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 应用场景 🔥
可以直接自定义别名!
// hook: useState
// const [counter, setCounter] = {counter: , setCounter:}
function useState(state: any) {
let currentState = state
const changeState = (newState: any) => {
currentState = newState
}
const tuple: [any, (newState: any) => void] = [currentState, changeState]
return tuple
}
const [counter, setCounter] = useState(10);
setCounter(1000)
const [title, setTitle] = useState("abc")
export {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 应用场景(优化) 🔥
// hook: useState
// const [counter, setCounter] = {counter: , setCounter:}
function useState<T>(state: T) {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
const info: [string, number] = ["abc", 18]
const tuple: [T, (newState: T) => void] = [currentState, changeState]
return tuple
}
const [counter, setCounter] = useState(10);
setCounter(1000)
const [title, setTitle] = useState("abc")
const [flag, setFlag] = useState(true)
// type MyFunction = () => void
// const foo: MyFunction = () => {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 函数的参数和返回值类型
// 给参数加上类型注解: num1: number, num2: number
// 给返回值加上类型注释: (): number
// 在开发中,通常情况下可以不写返回值的类型(自动推导)
function sum(num1: number, num2: number) {
return num1 + num2
}
// sum(123, 321)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
封装三方库时,可以写上,方便调用方看到
# 匿名函数的参数类型
// 通常情况下, 在定义一个函数时, 都会给参数加上类型注解的
function foo(message: string) {
}
const names = ["abc", "cba", "nba"]
// item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
// 上下文中的函数: 可以不添加类型注解
names.forEach(function(item) {
console.log(item.split(""))
})
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 对象类型
// Point: x/y -> 对象类型
// {x: number, y: number}
function printPoint(point: {x: number, y: number}) {
console.log(point.x);
console.log(point.y)
}
printPoint({x: 123, y: 321})
export {}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 可选类型 ?
// Point: x/y/z -> 对象类型
// {x: number, y: number, z?: number}
function printPoint(point: {x: number, y: number, z?: number}) {
console.log(point.x)
console.log(point.y)
console.log(point.z)
}
printPoint({x: 123, y: 321})
printPoint({x: 123, y: 321, z: 111})
export {}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 联合类型 |
// number|string 联合类型
function printID(id: number|string|boolean) {
// 使用联合类型的值时, 需要特别的小心
// narrow: 缩小
if (typeof id === 'string') {
// TypeScript帮助确定id一定是string类型
console.log(id.toUpperCase())
} else {
console.log(id)
}
}
printID(123)
printID("abc")
printID(true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 可选类型和联合类型的关系
// 让一个参数本身是可选的
// 一个参数一个可选类型的时候, 它其实类似于是这个参数是 类型|undefined 的联合类型
// function foo(message?: string) {
// console.log(message)
// }
function foo(message?: string) {
console.log(message)
}
foo()
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 类型别名 🔥
// type用于定义类型别名(type alias)
type IDType = string | number | boolean
type PointType = {
x: number
y: number
z?: number
}
function printId(id: IDType) {
}
function printPoint(point: PointType) {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 类型断言 as
有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)。
比如我们通过 document.getElementById,TypeScript只知道该函数会返回 HTMLElement ,但并不知道它具体的类型:
TypeScript只允许类型断言转换为 更具体(父转子) 或者 不太具体(unknown) 的类型版本,此规则可防止不可能的强制转换
// <img id="why"/>
// 1.类型断言 as
const el = document.getElementById("why") as HTMLImageElement
el.src = "url地址"
// 2.另外案例: Person是Student的父类
class Person {
}
class Student extends Person {
studying() {
}
}
function sayHello(p: Person) {
(p as Student).studying()
}
const stu = new Student()
sayHello(stu)
// 3.了解: as any/unknown
const message = "Hello World"
// const num: number = (message as unknown) as number
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 非空类型断言 !.
当我们编写下面的代码时,在执行ts的编译阶段会报错。这是因为传入的message有可能是为undefined的,这个时候是不能执行方法的。
但是,我们确定传入的参数是有值的,这个时候我们可以使用非空类型断言
非空断言使用的是! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测
// message? -> undefined | string
function printMessageLength(message?: string) {
// if (message) {
// console.log(message.length)
// }
// vue3源码
console.log(message!.length)
}
printMessageLength("aaaa")
printMessageLength("hello world")
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 可选链的使用 ?.
可选链事实上并不是TypeScript独有的特性,它是ES11(ES2020)中增加的特性:
- 可选链使用可选链操作符
?. - 它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行;
- 虽然可选链操作是ECMAScript提出的特性,但是和TypeScript一起使用更版本;
type Person = {
name: string
friend?: {
name: string
age?: number,
girlFriend?: {
name: string
}
}
}
const info: Person = {
name: "why",
friend: {
name: "kobe",
girlFriend: {
name: "lily"
}
}
}
// 另外一个文件中
console.log(info.name)
// console.log(info.friend!.name)
console.log(info.friend?.name)
console.log(info.friend?.age)
console.log(info.friend?.girlFriend?.name)
// if (info.friend) {
// console.log(info.friend.name)
// if (info.friend.age) {
// console.log(info.friend.age)
// }
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# !!运算符—boolean转换
将一个其他类型转换成boolean类型,类似于Boolean(变量)的方式,是ES11增加的新特性
const message = "Hello World"
// const flag = Boolean(message)
// console.log(flag)
const flag = !!message
console.log(flag)
1
2
3
4
5
6
7
2
3
4
5
6
7
# ??运算符—空值合并操作符
空值合并操作符(??)是一个逻辑操作符,当操作符的左侧是 null 或者 undefined 时,返回其右侧操作数, 否则返回左侧操作数
let message: string|null = "Hello World"
const content = message ?? "你好啊, 李银河"
// const content = message ? message: "你好啊, 李银河"
console.log(content)
1
2
3
4
5
2
3
4
5
# 字面量类型—结合联合类型
// "Hello World"也是可以作为类型的, 叫做字面量类型
const message: "Hello World" = "Hello World"
// let num: 123 = 123
// num = 321
// 字面量类型的意义, 就是必须结合联合类型
type Alignment = 'left' | 'right' | 'center'
let align: Alignment = 'left'
align = 'right'
align = 'center'
// align = 'hehehehe'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 字面量推理
// const info = {
// name: "why",
// age: 18
// }
// info.name = "kobe"
//
type Method = 'GET' | 'POST'
function request(url: string, method: Method) {}
type Request = {
url: string,
method: Method
}
const options = {
url: "https://www.coderwhy.org/abc",
method: "POST"
} as const
request(options.url, options.method)
export {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 类型缩小 🔥
什么是类型缩小呢?
- 类型缩小的英文是 Type Narrowing;
- 我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径;
- 在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小;
- 而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards);
常见的类型保护有如下几种:
- typeof
- 平等缩小(比如
===、!==) - instanceof
- in
- 等等...
// 1.typeof的类型缩小
type IDType = number | string
function printID(id: IDType) {
if (typeof id === 'string') {
console.log(id.toUpperCase())
} else {
console.log(id)
}
}
// 2.平等的类型缩小(=== == !== !=/switch)
type Direction = "left" | "right" | "top" | "bottom"
function printDirection(direction: Direction) {
// 1.if判断
// if (direction === 'left') {
// console.log(direction)
// } else if ()
// 2.switch判断
// switch (direction) {
// case 'left':
// console.log(direction)
// break;
// case ...
// }
}
// 3.instanceof
function printTime(time: string | Date) {
if (time instanceof Date) {
console.log(time.toUTCString())
} else {
console.log(time)
}
}
class Student {
studying() {}
}
class Teacher {
teaching() {}
}
function work(p: Student | Teacher) {
if (p instanceof Student) {
p.studying()
} else {
p.teaching()
}
}
const stu = new Student()
work(stu)
// 4. in
type Fish = {
swimming: () => void
}
type Dog = {
running: () => void
}
function walk(animal: Fish | Dog) {
if ('swimming' in animal) {
animal.swimming()
} else {
animal.running()
}
}
const fish: Fish = {
swimming() {
console.log("swimming")
}
}
walk(fish)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
编辑 (opens new window)
上次更新: 2022/03/29, 17:35:37