TypeScript-基础知识

2020/10/6 TypeScript
// typescript官网
1. www.tslang.cn
----------------------------------------------------------------------------------------------
// typescript的定义
1. typescript 是 javascript 的超集 // superset
2. typescript 有自己独特的静态类型 // javascript 是动态类型
3. typescript 不能直接在浏览器或node环境运行, 需要通过编译器编译成普通的 javascript 才能运行
----------------------------------------------------------------------------------------------
// typescript 相对于 javascript 的优势
1. typescript 的静态类型在开发过程中, 就能够发现潜在问题
2. 静态类型更友好的编辑器自动提示
3. 类型注解代码语义更清晰易懂
----------------------------------------------------------------------------------------------
// 运行 typescript
1. 运行 typescript 需要 node 环境, 但是不能直接运行后缀为ts的文件, 需要通过 tsc 编译成 js 文件
2. npm install typescript -g // 全局安装 typescript 即可使用 tsc 编译为 js 在 node 运行
    执行 tsc --init 可以把当前项目初始化成一个ts项目会多一个 tsconfig.json 文件
    在使用 tsc 对 ts 文件编译的时候会结合 tsconfig.json 配置文件进行编译
    如果使用 tsc index.ts 不会经过 tsconfig.json 配置文件, 只有直接运行 tsc 才会经过 tsconfig.json 的配置
    直接运行 tsc 会对根目录下的所有 ts 文件进行编译, 可以在 tsconfig.json 中写 files、include、exclude 来指定编译文件
    可以在 package.json 文件的 script 命令中写 "bulid": "tsc -w" 表示只要文件发生变化就会自动去编译
    还可以使用 "start": "nodemon node ./build/index.js" 监听编译后的 js 文件变化
    结合上面两个命令实现监听 ts 文件变化自动编译自动运行, 需要运行两个命令行
    如果只要运行一个命令就可实现两个命令的效果岂不是更好, npm install concurrently
    在 script 中 "dev": concurrently npm run bulid & npm run start
3. 每次都要手动执行 tsc index.ts 然后再 node index.js 会比较麻烦, 可以安装 ts-node // 结合了 tsc 和 node 两个命令,不会生成 js 文件
4. npm install ts-node -g // 全局安装 即可直接运行 ts-node index.ts
5. 在 ts 文件中引入第三方的npm, 如果npm包是 js 语法写的则会导致引入错误不识别
   如在node中安装 npm install superagent 这个包用来发ajax, 但是在 ts 文件中写则会报错
   还需要再安装 npm install @type/superagent 来翻译 npm 下载的 js 文件
   相当于 .ts = .js + .d.ts // d.ts 即翻译文件即 @type/superagent
----------------------------------------------------------------------------------------------
// 静态类型
1. 一个变量是静态类型不仅意味着这个变量类型不能修改还意味着这个变量的属性和方法也确定了
   正因为这样编辑器才会给我们更友好的提示
----------------------------------------------------------------------------------------------
// 类型注解和类型推断
1. 类型注解就是我们来告诉 typescript 变量是什么类型 // 需要在写代码时加上上面的基础类型或对象类型
2. 类型推断就是 typescript 会自动的尝试去分析变量的类型 // 跟正常js代码一样, 鼠标移到变量上去会显示类型
3. 如果 typescript 能够自动分析变量类型, 我们就什么也不需要做
4. 如果 typescript 无法分析变量类型的话, 我们就需要使用类型注解
5. typescript 就是让我们所有的变量和属性都有类型, 如果推断不出来就自己加即类型注解
6. 示例:
    const num1 = 1 // 不需要加类型注解, 因为值是固定的
    const num2 = 2 // 不需要加类型注解, 因为值是固定的
    const total = num1 + num2 // 不需要加类型注解
    let num3: number // 需要加
    num3 = 3
    function getTotal(num1: number, num2: number) { // 需要加, 因为推断不出来参数是什么类型
      return num1 + num2
    }
    const total = getTotal(1, 2) // 不需要加, 因为函数里面已经推断
    const obj = { name: 'chenj', age: 23 } // 不需要加, 和num1、num2一个道理, 值是固定的, 显式的赋值了
----------------------------------------------------------------------------------------------
// 基础类型和对象类型
1. 基础类型:
    const num: number = 123 // 数字
    const str: string = 'chenj' // 字符串
    const strOrNum: string | number = 'chenj' // 可以是字符串也可以是数字
    const num: number = null // null 和 undefined 可以复制给任意类型
    const num: number = undefined // null 和 undefined 可以赋值给任意类型
    nullundefined 是所有类型的子类型,可以赋值给其他类型
    nullundefined 只能赋值给void和它们各自
    any、nullundefined、symbol、boolean、void 这些都属于基础类型
2. 对象类型:
    object 使用 'interface' 定义  // 对象
    object 表示一个空对象即 {}
    Object 表示构造函数
    const obj: object = { name: 'chenj', age: 22 } // 如果直接使用 object 定义,调用 obj.name 则报错,相当于 object === {},需使用 interface
    const arr: number[] = [1, 2, 3] // 数组
    class Person {} // class
    const chenj: person = new Person() // class
    const date = new Date() // date
    const json: object = JSON.parse({ "name": "chenj" }) // 通过JSON.parse方法返回的都是any, 需要加类型注解
3. 字面量:
  const str: 'name' = 'name' // 只能赋值成 name,其他值不行
  const num: 1 = 1 // 只能赋值成 1,其他值不行
----------------------------------------------------------------------------------------------
// 函数
1. 函数: 参数一般需要类型注解, 返回值一般可以通过类型推断出来不用显式的去写
    const getTotal1 = (str: string): number => { // 函数写法1, 这里的返回值注解可以不写, 因为通过推断可以得出parseInt返回number
      return parseInt(str, 10)
    }
    const getTotal2: (str: string) => number = (str) => { // 函数写法2
      return parseInt(str, 10)
    }
    function sayHello(): void { // 无返回值
      console.log('hello')
    }
    function errorEmitter(): never { // 函数无法执行到最后
      throw new Error()
      console.log('hello')
    }
    function add1(num1: number, num2: number): number { // 返回值为number
      return num1 + num2
    }
    function add2({ num1, num2 }: { num1: number, num2: number }): number { // 解构的类型注解
      return num1 + num2
    }
2. 在调用函数时如果传入的是一个字面量对象 ts 会进行强校验,如果传入的是对象引用则不会
  interface IPerson {
    name: string
    age: number
  }

  function getUserInfo(obj: IPerson) {
    return `${obj.name} -- ${obj.age}`
  }

  const obj = {
    name: 'chenj',
    age: 23,
    sex: '男'
  }

  getUserInfo(obj) // 正确
  getUserInfo({
    name: 'chenj',
    age: 23,
    sex: '男' // 报错
  })
3. 函数重载,在TypeScript中对于函数重载的理解是:只要函数参数的类型或者函数参数的数量不同时,就可以认为这是两个函数(重载)。
   在有函数重载时,会优先从第一个进行逐一匹配,因此如果重载函数有包含关系,应该将最精准的函数定义写在最前面。
  // 前两个为函数声明,最后一个才是函数实现
  function add (a: number, b: number): number;
  function add (a: string, b: string): string;
  function add (a: number | string, b: number | string): number | string {
    if (typeof a === 'number' && typeof b === 'number') {
      return a + b
    } else {
      return a + '' + b
    }
  }
  console.log(add(1, 2))      // 3
  console.log(add('1', '2'))  // 12
----------------------------------------------------------------------------------------------
// 声明合并
1. 如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型。 声明合并,我们在上面已经有实例的案例了,那就是函数的重载。
2. 当重复定义同一个接口时,会进行接口合并
  interface Person {
    name: string,
    address: string
  }
  interface Person {
    name: string,
    age: 23
  }
  // 相当于
  interface Person {
    name: string,
    address: string,
    age: 23
  }
3. 当合并的属性类型不一致时,会报错
  interface Person {
    name: string,
    address: string
  }
  interface Person {
    // 报错,name类型冲突
    name: number,
    age: 23
  }
----------------------------------------------------------------------------------------------
// 数组和元组
1. 数组:
    const arr: number[] = [1, 2, 3]
    const arr: (number | string)[] = [1, '2', 3]
    const arr: { name: string, age: number }[] = [{ name: 'chenj', age: 23 }] // 数组的每一项必须有 name 和 age
    type User = { name: string, age: number } // type alias 类型别名
    const arr: User[] = [{ name: 'chenj', age: 23 }] // 数组的每一项必须有 name 和 age
    class Teacher { name: string, age: number }
    const arr: Teacher[] = [new Teacher(), { name: 'chenj', age: 23 }] // 因为第二项里面的内容和Teacher类里面的内容一致, 所以可以, 多或少都不行
2. 元组:
    const arr1: [string, string, number] = ['chen', 'jie', 23] // 不能多也不能少
    const arr2: [string, string, number][] = [['chen1', 'jie1', 23], ['chen2', 'jie2', 23]]
    要求每一项的值类型和定义的相同, 个数也要和定义的类型个数相同 // tuple
    它可以使用和数组一样的 API,可以使用 API 突破长度限制
    arr1.push(1) // arr = ['chen', 'jie', 23, 1] 成功
    arr1.push(true) // 错误
----------------------------------------------------------------------------------------------
// interface接口, 是 ts 帮助我们校验语法的工具, 在编译成 js 后会全部剔除
type Person = string // interface无法表示基本类型, type别名可以, 一般使用interface
interface Person {
  readonly name: string // readonly表示只读, 这个变量只能读不能改, 变量用 const 属性用 readonly
  age?: number // 加问号表示age这个变量可有可无, 可以传也可以不传
  [propName: string]: any // 表示除了name和age还可以传其他的变量
  say(): string // 表示需要一个say函数并且返回的是string
  (name: string, age: number): boolean // 表示函数参数是 string 和 number 返回值是 boolean
  [index: number]: string // 表示数组索引
}
interface Teacher extends Person { // 继承Person中的内容, 外加自己的teach方法
  teach(): string
}

const getPersonName = (obj: Person): void => { // 表示函数的参数内容必须是name和say和其他
  console.log(obj.name)
}
const obj = { name: 'chenj', age: 23, say() { return 'hello' }}
getPersonName(obj)
getPersonName({ name: 'chenj', age: 23 }, say() { return 'hello' }) // 虽然和上面的执行一致, 但是如果obj中加了一个interface中没有的变量, 这种字面量的传递方式会报错, 而上面传递变量名的方式则不会, 传递字面量ts会进行强校验

class User implements Person { // 表示类应用person这个接口, 上面必须的只要name和say, class用 implements,可以有多个,用逗号隔开
  name = 'chenj'
  say() {
    return 'hello'
  }
}
----------------------------------------------------------------------------------------------
// 类的定义和继承
class Person {
  name = 'chenj'
  getName() {
    return this.name
  }
}
class Teacher extends Person {
  getTeacherName() {
    return 'teacherName'
  }
  getName() { // 和父类的方法重复, 重写
    return super.getName() + this.name // 使用super调用父类的方法, 使用this.name获取父类的属性
  }
}
----------------------------------------------------------------------------------------------
// 类中的访问类型和构造器
// public 允许在类的内部和外部被调用, 不写默认为pulic
// private 允许在类的内部被调用
// protected 允许在类的内部和继承的子类中被调用
// readonly 表示只读不可修改, 在属性前面加
// constructor 会在实例化的时候被自动执行
class Person {
  public name: string
  constructor(name: string) {
    this.name = name
  }
}
class Person { // 上面的写法可以写成这样的简写
  constructor(public name: string) {}
}
class Teacher extends Person { // 构造器继承
  constructor(public age: number) {
    super('chenj') // 如果父类没有constructor而子类有就必须执行super方法
  }
}
const person = new Teacher(23)
console.log(person.name)
----------------------------------------------------------------------------------------------
// getter、setter、static
class Person {
  constructor(private _name: string) {} // 默认前面是public
  get name() {
    return this._name + 'chenj'
  }
  set name(name: string) {
    this._name = name
  }
  public static getName() { // 静态方法通过类名调用而不是实例化, 默认前面是public, 可不写
    return this._name
  }
}
console.log(Person.getName())
----------------------------------------------------------------------------------------------
// 抽象类, 只能被继承, 不能被实例化
abstract class Geom {
  width: number
  getType() {
    return 'Geom'
  }
  abstract getArea(): number // 抽象方法, 没有实现
}
class Circle extends Geom {
  getArea() { // 必须实现抽象类中没有实现的方法
    return 100
  }
}
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
Last Updated: 2024/6/11 14:20:38
    飘向北方
    那吾克热-NW,尤长靖