这篇文章主要是读 ts 入门教程 与 ts 中文网 所记下来的一些笔记,作为后面学习的一个参考点;看完文章后,对知识点做一定的提取;但是描述起来比较简单,主要针对部分关键知识点;如果需要系统的学习的话,就需要把教程看了。
类型声明
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
|
let num: number
num = 1
num = 'str'
let str: string = 'str'
let value: number | string
value = 1
value = 'str'
let value: any
value = true
value = { foo: 'foo' }
function foo (bar: string | number): boolean {
var flag:boolean = true
console.log(bar.length)
console.log(bar.toString())
return flag
} |
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
|
interface Person {
name: string,
age: number
}
let person: Person = {
name: 'kyrie',
age: 20
}
// 当有属性是不确定添加,部分对象包含,部分对象不包含的情况,可以使用这样子来表示:
interface Person {
name: string,
nickName?: string,
age: number,
id?: number,
}
let person: Person = {
name: 'kyrie',
age: 20,
id: 10
}
// 当一个对象需要一些不确定名称的值,可以使用任意属性的定义方法,但是已知的属性必须是任意属性类型的子集;例如已知属性全部都是 string 类型;那么任意属性可以定义为 string, 但不能够定义为 number;如果已知属性有number, string,那么任意属性定义为 string 也不正确
interface: Person {
readonly id: number,
name: string,
age: number,
[propName: string]: any // 所以这里可以定义为 any 类型;那么 string 与 number 就是 any 的子集
}
// 注意上面的接口有一个是 readonly 关键词,表示只读,也就是第一次赋值,不能二次赋值
let person: Person = {
id: 10,
name: 'kyrie',
age: 20
}
person.id = 11 // error |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
let arr: number[] = [1, 2, 3]
let arr: Array<number> = [1, 2, 3]
interface NumberArray {
[index: number]: number
}
let arr: NumberArray = [1, 2]
let arr2: NumberArray = [1, 2, '3']
function foo () {
let args: IArguments = arguments
console.log(args)
console.log(typeof args)
} |
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
|
// 通常函数定义分两种,一种是函数声明,另外一种是函数表达式
function foo () {} // 函数声明
let foo = function () {} // 函数表达式
// 对于普通函数声明:
function foo (id: number, name: string = 'anonymous', age?: number, address?: string) {}
// 上面这个函数表示,id 这个参数比必选的,name 参数有默认值,如果不传的值是 'anonymous', age 参数是可选的,不传就没有值了;
// 可选参数且没有默认值的参数,只能在函数声明的最后,可以有多个可选没默认值参数;有默认值的参数,后面可以接必须参数
// 剩余参数的处理;实际上剩余参数是数组,我们可以声明数组的方式来处理
function foo (id: number, name: string, ...msg: any[]) {
console.log(msg)
}
// 对于函数表达式
let foo = function (id: number, name: string): any {
// 这样子定义是ok的,能够通过编译
}
// 实际上只是对右边的匿名函数进行声明类型,没有对 foo 的变量的类型声明,完整的声明方式应该是这样子的:
// 注意区分箭头函数的区别
let foo: (id: number, name: string) => any = function (id: number, name: string): any {
// xxx
}
// 另外一种为函数表达式变量定义类型的方式可以使用接口形式
interface fooFunction {
(id: number, name: string): boolean
}
let foo: fooFunction = function (id: number, name: string): boolean {
return !!id
}
// 函数重载,需求:假设对传的参数是number,则扩大十倍,是字符串则添加前缀:
function calc (value: number | string): number | string {
if (typeof value === 'number') {
return value * 10
} else {
return '_' + value
}
}
// 实际上是应该对参数是number 则返回值是 number; 参数是 string ,则返回值是 string
function calc (value: number): number;
function calc (value: string): string;
function calc (value: number | string): number | string {
if (typeof value === 'number') {
return value * 10
} else {
return '_' + value
}
}
// 前两次声明是函数的定义,最后的声明是函数的实现;ts 会从最开始声明的进行匹配 |
- 类型断言
类型断言就是 ts 允许开发者覆盖它的推断,并且能以你任何你想要的方式分析它,这种机制被称为「类型断言」。
1
2
3
4
5
6
7
8
|
function getLength (value: string | number): void {
console.log((<string>value).length)
console.log((value as string).length)
} |
简要解释一下上面的函数,因为接收的参数有string
与number
两种类型,而number
类型是没有length
属性,前面说到,需要两个类型共有的方法或者属性才能使用;而我们这里强制断言为string
,因此编译的时候不会报错。
就例如上面的getLength
方法,有些时候我们需要定义多个类型的时候,如果经常要重复编写就会很麻烦,我们可以自定义一个类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
type numStr = string | number
type numStrFun = numStr | () => string
function getLength (value: numStr) {
}
type top3 = 'first' | 'second' | 'third'
function typeFun (value: top3) {
console.log(value)
}
typeFun('first')
typeFun('fourth') |
- 元组类型
通常数组是表示同一类型的元素,而元组(Tuple)则可以表示不同类型;
1
2
3
4
5
6
7
8
9
10
11
|
var multiType: [string, number]
multiType = ['1', 1]
multiType[0] = 2
multiType.push('2')
multiType.push(2)
multiType.slice(0)
multiType.push(true) |
枚举
通常是用于取值在一定范围的场景,例如一周7天,颜色固定红绿蓝三个颜色
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
|
enum Color {
red,
green,
blue
}
Color['red']
Color[1]
var Color
(function (Color) {
Color[Color["red"] = 0] = "red"
Color[Color["green"] = 1] = "green"
Color[Color["blue"] = 2] = "blue"
})(Color || (Color = {}))
var Color = {
0: 'red',
1: 'green',
2: 'blue',
'red': 0,
'green': 1,
'blue': 2
}
|
实际上枚举的步长每次增加1;如果我们设置初始的值是1,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
enum Color {
red = 1,
green,
blue
}
Color[2] // 'green'
Color[3] // 'blue'
// 所以这个数可以是负数或者是小数,每次增加步长都是1;也可以是计算所得值:
enum Color {
red,
green,
blue = 'blue'.length
}
// 需要注意的是,这个计算所得值对应的枚举项需要是最后的值;如果不是最后的值,那么计算所得值后面的枚举项将不能每次步长+1;无法获取确切的初始值而报错。 |
- 类
ts 的类与 es6 的类有比较多相同的地方;增加的地方有:
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
|
class Animal {
public name
private nickName
constructor (name: string, nickName: string) {
this.name = name
this.nickName = nickName
}
getName () {
return this.name
}
private getNickName () {
return this.nickName
}
protected getFullName () {
return this.name + ':' + this.nickName
}
}
abstract class Eat {
abstract eatFood ()
}
class Animal {
public food
constructor (food: string) {
this.food = food
}
eatFood () {
console.log('eat food:', this.food)
}
} |
类与接口 (interface)
通常类大多数都是继承关系,子类通过继承父类,然后加多一些特有的方法属性等;但有时候继承类并不能实现所有方法,子类(假设为A)可能有些方法需要在别的类(假设为B类)实现;而且这个B类也为其他类别(假设为C类)提供;B类就有点公共类的意思了,同时为A,C类提供;
例如一个场景:
举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它:
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
|
interface Alarm {
alert ()
}
class Door {
}
class SecurityDoor extends Door implements Alarm {
alert () {
console.log('SecurityDoor alert')
}
}
class Car implements Alarm {
alert () {
console.log('Car alert')
}
}
// 并且一个类可以实现多个接口:
interface Alarm {
alert()
}
interface Light {
lightOn ()
lightOff ()
}
class Car implements Alarm, Light {
alert() {
console.log('Car alert')
}
lightOn() {
console.log('Car light on')
}
lightOff() {
console.log('Car light off')
}
} |
上例中,Car 实现了 Alarm 和 Light 接口,既能报警,也能开关车灯
这样子在使用 class 语法的时候,能够提高类的灵活性;容易添加部分公共类方法,扩充对象
泛型
通常在定义函数、接口、类的时候,会定义一些类型;但有时候有些类型是函数执行的时候才能确定,并且是根据传入参数的类型,返回的类型也要一致;那样子就有点存在动态类型的意思了;而泛型的作用是可以在定义的时候不预先指定类型,在运行的时候再确定好数据的类型:
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
|
function repeatValue<T> (value: T): T[] {
var arr: T[] = []
for (var i = 0; i < 3; i++) {
arr.push(value)
}
return arr
}
repeatValue('str')
repeatValue(1)
function reverse<T, U> (v1: T, v2: U): [U, T] {
return [v2, v1]
}
reverse('1', 2)
function getLen<T = string> (value: T): number {
return value.length
}
interface lengthInterface {
length: number
}
function getLen<T extends lengthInterface> (value: T): number {
return value.length
}
interface includeFunction {
(str: string, subStr: string): boolean
}
let isInclude: includeFunction
isInclude = function (str: string, subString): boolean {
return str.indexOf(subStr) > -1
}
interface repeatFunction {
<T>(value: T, len: number): Array<T>
}
let repeatByLen: repeatFunction
repeatByLen = function<T> (value: T, len: number): Array<T> {
let arr: Array<T> = []
for (let i = 0; i < len; i++) {
arr.push(value)
}
return arr
}
repeatByLen('foo', 3)
class NumberClass<T> {
value: T,
add: (x: T, y: T) => T
}
let num = new NumberClass()
num.value = 10
num.add = function (x, y) { return x + y } |