Typescript是什么?

  • JavaScript的超集
  • 静态类型检查
  • 兼容ES6以上的语法
  • 使代码更易读
  • 编译前就能发现大量错误,减少低级错误的发生

安装和使用

安装 typescript

  1. npm install -g typescript
  2. # 安装特定版本
  3. npm install -g typescript@版本号

查看版本

  1. tsc -v

编译 ts 文件,生成 js 文件

  1. tsc xxx.ts

ts-node

每次先 tsc 编译 ts 文件,再用 node 运行 js 文件太麻烦,使用 ts-node 可以将这两步合二为一

  1. # 安装
  2. npm install -g ts-node
  3. # 运行
  4. ts-node xxx.ts

image.png
如果使用ts-node如上图报错,执行这条命令

  1. npm install -D tslib @types/node

基础类型

  1. // 基础类型
  2. let isDone: boolean = false
  3. let age: number = 20
  4. let binaryNumber: number = 0b1111
  5. let firstName: string = 'hello'
  6. let u: undefined = undefined
  7. let n: null = null
  8. // 在typescript中, null和undefined都有各自的类型
  9. // undefined和null都可以是所有类型的子类型
  10. // undefined一般赋值给基础类型,null一般赋值给对象

Any和联合类型

  1. // any
  2. let notSure: any = 4
  3. notSure = true
  4. notSure.myName
  5. // 联合类型 union
  6. let numberOrString: number | string = 100
  7. numberOrString = 'hello'

Array和Tuple

  1. // Array
  2. let arr: number[] = [1, 2, 3, 4]
  3. // Tuple
  4. let t: [string, number] = ['a', 1]

Interface

  1. // Interface
  2. // 对对象的形状进行描述
  3. interface Person {
  4. readonly id: number
  5. name: string
  6. age: number
  7. address?: string
  8. }
  9. const obj: Person = {
  10. id: 1,
  11. name: 'jack',
  12. age: 21,
  13. }

函数类型和类型推断

  1. function add(x: number, y: number, z?: number): number {
  2. if (typeof z === 'number') {
  3. return x + y + z
  4. } else {
  5. return x + y
  6. }
  7. }
  8. const add2: (x: number, y: number, z?: number) => number = add

  • 类( Class ): 定义了一切事物的抽象特点
  • 对象( Object ): 类的实例
  • 面向对象( OOP )三大特性: 封装、继承、多态

声明一个类

  1. class Animal {
  2. name: string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. run() {
  7. return `${this.name} is running`
  8. }
  9. }
  10. console.log(typeof Animal) // function
  11. const snake = new Animal('lily')
  12. console.log(snake.run())

转译成 JS, 可以看到 class 的本质是 function, 创建实例后,实例会拥有属性name,而 run 方法在原型上

  1. var Animal = /** @class */ (function () {
  2. function Animal(name) {
  3. this.name = name;
  4. }
  5. Animal.prototype.run = function () {
  6. return this.name + " is running";
  7. };
  8. return Animal;
  9. }());
  10. console.log(typeof Animal); // function
  11. var snake = new Animal('lily');
  12. console.log(snake.run());

继承

新增方法
  1. class Dog extends Animal {
  2. // 没重写constructor则使用父类的constructor
  3. bark() {
  4. return `${this.name} is barking`
  5. }
  6. }
  7. const xiaobao = new Dog('xiaobao')
  8. console.log(xiaobao.bark())

重写父类的constructor和方法
  1. class Cat extends Animal {
  2. constructor(name: string) {
  3. super(name)
  4. console.log(this.name)
  5. }
  6. // 重新父类的方法
  7. run(): string {
  8. return 'Meow, ' + super.run() // super. 调用父类的方法
  9. }
  10. }
  11. const meme = new Cat('meme')
  12. console.log(meme.run())

修饰符

public 运行外部访问,为默认值

  1. class Animal {
  2. public name: string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. run() {
  7. return `${this.name} is running`
  8. }
  9. }

private 不允许任何人访问
protected 外部不能访问,但子类可以访问
readonly 外部能访问,但不能修改

静态属性和方法

静态属性和方法是直接在类本身上的,不需要新建实例就能访问

  1. class Animal {
  2. static categories: string[] = ['mammal', 'bird']
  3. static isAnimal(a) {
  4. return a instanceof Animal
  5. }
  6. name: string
  7. constructor(name: string) {
  8. this.name = name
  9. }
  10. run() {
  11. return `${this.name} is running`
  12. }
  13. }
  14. console.log(Animal.categories)
  15. const snake = new Animal('lily')
  16. console.log(Animal.isAnimal(snake))

类和接口

如果两个类都有相同的方法,但又不好通过继承来定义,则可以使用interface

  1. interface Radio {
  2. swicthRadio(): void
  3. }
  4. interface Battery {
  5. checkBatteryStatus(): void
  6. }
  7. class Car implements Radio {
  8. color: string
  9. swicthRadio(): void {}
  10. }
  11. class Cellphone implements Radio, Battery {
  12. brand: string
  13. swicthRadio(): void {}
  14. checkBatteryStatus(): void {}
  15. }

interface也可以继承

  1. interface Radio {
  2. swicthRadio(): void
  3. }
  4. // interface Battery {
  5. // checkBatteryStatus(): void
  6. // }
  7. interface RadioWithBattery extends Radio {
  8. checkBatteryStatus(): void
  9. }
  10. class Cellphone implements RadioWithBattery {
  11. brand: string
  12. swicthRadio(): void {}
  13. checkBatteryStatus(): void {}
  14. }

type 也可以被 implements, interface 和 type 最大的区别是 interface 声明可以合并

  1. type Radio = {
  2. swicthRadio(): void
  3. }
  4. interface RadioWithBattery extends Radio {
  5. checkBatteryStatus(): void
  6. }
  7. interface RadioWithBattery {
  8. meow(): void
  9. }
  10. class Car implements Radio {
  11. color: string
  12. swicthRadio(): void {}
  13. }
  14. class Cellphone implements RadioWithBattery {
  15. brand: string
  16. swicthRadio(): void {}
  17. checkBatteryStatus(): void {}
  18. meow(): void {}
  19. }

枚举 enum

常规用法

  1. enum Directions {
  2. up,
  3. left,
  4. right,
  5. down,
  6. }
  7. console.log(Directions.up)

如果子项不赋值,则默认从0开始

  1. // 编译成JS后
  2. var Directions;
  3. (function (Directions) {
  4. Directions[Directions["up"] = 0] = "up";
  5. Directions[Directions["left"] = 1] = "left";
  6. Directions[Directions["right"] = 2] = "right";
  7. Directions[Directions["down"] = 3] = "down";
  8. })(Directions || (Directions = {}));
  9. console.log(Directions.up);

如果第一项赋值为number, 则后面项依次递增

  1. enum Directions {
  2. up = 100,
  3. left,
  4. right,
  5. down,
  6. }
  7. console.log(Directions.up)
  1. // 编译成JS后
  2. var Directions;
  3. (function (Directions) {
  4. Directions[Directions["up"] = 100] = "up";
  5. Directions[Directions["left"] = 101] = "left";
  6. Directions[Directions["right"] = 102] = "right";
  7. Directions[Directions["down"] = 103] = "down";
  8. })(Directions || (Directions = {}));
  9. console.log(Directions.up);

第一项赋值非number, 则其他项也都要赋值

  1. enum Directions {
  2. up = 'UP',
  3. left = 'LEFT',
  4. right = 'RIGHT',
  5. down = 'DOWN',
  6. }
  7. console.log(Directions.up)

常量枚举

  1. const enum Directions {
  2. up = 'UP',
  3. left = 'LEFT',
  4. right = 'RIGHT',
  5. down = 'DOWN',
  6. }
  7. const value = 'UP'
  8. if (value === Directions.up) {
  9. console.log('go up')
  10. }

编译后会发现枚举的声明都被舍弃了,使用常量枚举可以提升性能

  1. var value = 'UP';
  2. if (value === "UP" /* up */) {
  3. console.log('go up');
  4. }

泛型

基本用法

在声明一个函数时,我们不希望指定传入参数和返回值的具体类型,而是等实际使用时才知道具体的类型,且我们希望函数传入参数的类型和返回值的相同或具有关联,则我们应该使用泛型( generics )

  1. function echo<T>(arg: T):T{
  2. return arg
  3. }

<>中的 T 称为类型变量, T 帮助我们捕获用户传入的类型,之后再使用这个类型
在使用函数的时候我们可以明确指定 T 的类型

  1. const result = echo<string>('hello')

或者不指定 T 的类型,只传入参数, Typescript会根据参数推断 T 的类型( 推荐 )

  1. const result = echo(123)

数组和元组

  1. // 数组
  2. function len<T>(arr: T[]):T[] {
  3. console.log(arr.length)
  4. return arr
  5. }
  6. // 或者
  7. function len<T>(arr: Array<T>):Array<T> {
  8. console.log(arr.length)
  9. return arr
  10. }
  11. // 元组
  12. function swap<T, U>(tuple:[T, U]): [U, T]{
  13. return [tuple[1], tuple[0]]
  14. }
  15. const result = swap(['hello', 123])

约束泛型

如果我们希望传入的参数只要有length属性就行,而不是只能传入数组
在类型参数中使用extends就能解决这个问题

  1. interface IWithLength {
  2. length: number
  3. }
  4. function echoWithArray<T extends IWithLength>(arg: T):T {
  5. console.log(arg.length)
  6. return arg
  7. }
  8. echoWithArray([1, 2, 3])
  9. echoWithArray({length: 10})
  10. echoWithArray('hello')

类和接口

在 class 和 interface 中也能使用泛型
T 就是一个类型接收器,等待用户传入真正的类型

在 class 中使用泛型

  1. class Queue<T> {
  2. private data = []
  3. push(item: T) {
  4. return this.data.push(item)
  5. }
  6. pop(): T {
  7. return this.data.shift()
  8. }
  9. }
  10. const queue = new Queue<number>()
  11. queue.push(1)
  12. queue.push('str') // 报错
  13. console.log(queue.pop().toFixed())
  14. const queue2 = new Queue<string>()
  15. queue2.push('str')
  16. console.log(queue2.pop().length)

interface中使用泛型

  1. interface KeyPair<T, U> {
  2. key: T
  3. value: U
  4. }
  5. let kp1: KeyPair<number, string> = {key: 1, value: 'str'}
  6. let kp2: KeyPair<string, number> = {key: 'str', value: 123}

TypeScript中内置了很多的接口,比如 Array

  1. let arr: Array<number> = [1, 2, 3]

interface 还可以用来定义函数类型

  1. interface IPlus<T> {
  2. (a: T, b: T): T
  3. }
  4. function plus(a: number, b: number): number {
  5. return a + b
  6. }
  7. function connect(a: string, b: string): string {
  8. return a + b
  9. }
  10. const a: IPlus<number> = plus
  11. const b: IPlus<string> = connect

类型别名和类型断言

使用 type 关键字定义类型别名( type aliases)

  1. type PlusType = (x: number, y: number) => number
  2. function sum(x: number, y: number): number {
  3. return x + y
  4. }
  5. const sum2: PlusType = sum
  6. type NameResolver = () => string
  7. type NameOrResolver = string | NameResolver
  8. function getName(n: NameOrResolver): string {
  9. if (typeof n === 'string') {
  10. return n
  11. } else {
  12. return n()
  13. }
  14. }

使用 as 关键字类型断言,告诉TS我比你更懂这个变量

  1. function getLength(input: string | number):number {
  2. const str = input as String
  3. if (str.length) {
  4. return str.length
  5. } else {
  6. const number = input as Number
  7. return number.toString().length
  8. }
  9. }

类型断言还可以简写:

  1. if((<string>input).length) {
  2. return (<string>input).length
  3. } else {
  4. return input.toString().length
  5. }

声明文件

参考文档
当使用第三方库时,因为没有类型声明,TS无法识别
我们可以自己添加声明,新建以 .d.ts 结尾的文件,在其中添加类型声明

  1. // src/jQuery.d.ts
  2. declare var jQuery: (selector: string) => any;
  1. // src/index.ts
  2. jQuery('#foo');

如果声明了还是无法识别,在TS的配置文件 tsconfig.json 中添加如下配置:

  1. {
  2. "include": ["**/*"]
  3. }

大多数第三方库都有了官方的声明文件,只需要npm安装即可, 例如

  1. npm install --save @types/jquery