Skip to content
On this page

TypeScript 初识

TIP

TypeScript 是 JavaScript 的超集,它可以编译成纯 JavaScript。用来限制变量、常量的类型。
注意:通常情况下 Ts 文件无法被直接运行,需要被编译成 js 文件。

安装 TypeScript

shell
npm install -g typescript

全局安装 typeScript 之后,在全局就可以执行 tsc 命令编译 js 文件了

执行 tsc --init 生成 tsconfig.json,用来对编译的选项进行配置

编译 ts 文件

shell
tsc demo.ts //  demo.ts 文件编译成 js

TypeScript 类型控制

在 ts 中,在为一个变量定义了数据类型之后,这个变量就不能再更改类型,在重新赋值的时候也必须是这个类型

类型推论

在声明一个变量时,如果没有指定这个这个变量的类型,那 ts 会隐式的推断类型

ts
let a = 124

// 相当于
let a: number = 124

所以上面的例子中,不能再为变量赋予 number 之外的其他类型的值了,如果声明变量的时候没有为它赋值,则会隐式定义类型为 any

原始数据类型

定义常见原始数据类型:boolean、number、string

ts
let a: boolean = true
let b: number = 123
let b2: number = NaN
let b3: number = Infinity
let c: string = "abc"
let d: string = `name is ${name}`

但是如果我们改下,改成下面的例子

ts
let e: boolean = new Boolean(true) // 编译失败

实际上这个 new Boolean 实例是一个 object,而不是我们期望的 boolean 值,如果期望这个变量是一个布尔对象的话,需要这样写

ts
let e: Boolean = new Boolean(true)

number、string 同理

void

当一个函数没有返回值时,可以将返回值的类型定为 void

ts
function warnUser(): void {
  console.log("This is my warning message")
}

定义为 void 没有什么意义,只能为它赋值为 null 或 undefined

null 和 undefined

用 null 或者 undefined 来定义类型的时候,也只能为它赋予 null 或者 undefined 的值

ts
let a: null = null
let b: undefined = undefined

unknown

当变量类型允许变化时,可以定义为 unknown

ts
let a: unknown = "124"

a = 124 // 此时a 的类型允许从字符串转变为 数字

any

如果暂时还不确定某个变量的类型,就可以定为 any,这时候这个变量的类型是可以动态变化的,在这个变量上访问任何属性和方法都是被允许的

ts
let f: any = 4
f = "hello"

TODO: unknown 和 any 的区别

联合类型

联合类型可以表示变量为多种类型的其中一种,用 | 隔开

ts
let a: string | number = 24

为变量声明了 string 和 number 类型之后,可以为这个变量赋予这两个之一类型的值,如上面的代码中,赋予了 number 的值之后,那么就不能再使用 number 类型没有的方法、属性了,如果想访问 a.length 那么是会报错的

当 ts 不知道类型时,只能使用共有的方法、属性

ts
function (x: number | string){
  return x.length   // 会报错的。因为 x 有可能是 number 类型没有 length 属性
}

Literal Types

如果需要约束字符串、数字只能是某几个值中的某个,那么可以用 Literal Types

ts
type gender = "male" | "female"
type age = 10 | 20
function getGender(gender: gender, age: age): string {
  switch (gender) {
    case "male":
      return ""
    case "female":
      return ""
  }
}

上面的例子中,在调用这个函数时,gender 和 age 都只能在给定的值中选中一个,输入其他值时 ts 会报错

数组

同类型数组

一个纯数字数组 number[]或者 Array<number>来表示

ts
let e: number[] = [1, 2, 3] // 纯数字数组
let f: Array<number> = [1, 2, 3] // 纯数字数组

一个比较常见的做法是,用 any 来允许数组内允许出现任意类型

ts
let a: any[] = [123, "tom", { gender: "male" }]

元组

上面的数组只能用来表示数组项目为同类型的情况,如果数组内允许出现不同类型的情况,在 ts 中称为元组

ts
let g: [string, number] = ["aaa", 21]

函数

有两种声明函数的方式,函数声明和函数表达式

函数声明

ts
function fn(firstName: string, lastName: string): string {
  return `${firstName} ${lastName}`
}

需要同时对函数的输入、输出都进行限制,不符合个数的参数是不被允许的

函数表达式

函数表达式的写法我们可以写成

ts
let fn = function(x: number, y: number): number {
  return x + y
}

这样可以通过编译,但这是 ts 通过类型推断自己推断出来的,完整的写法如下所示

ts
let fn: (x: number, y: number) => number = function(x: number, y: number): number {
  return x + y
}

接口定义函数

ts
interface Func {
  (x: number, y: number): number
}

let fn: Func = function(x: number, y: number): number {
  return x + y
}

我们先给 fn 定义了 Func 的接口类型,那么在这个变量赋值的时候,就只能赋予这个变量符合的类型值

可选参数

如果需要可选参数的话,那可以用 ? 表示,可选参数必须要接在必传参数后面

ts
function fn(firstName: string, lastName?: string): string {
  return `${firstName} ${lastName}`
}

fn("124")

函数重载

TODO:

枚举:enum

枚举可以类比成一个对象,与对象不一样的地方是,对象只能通过 key 访问到 value。而枚举不仅能通过 key 访问 value,还能够通过 value 访问到 key 值

ts
enum num {
  one,
  two,
  three,
}
console.log(num[one])

如果没有给枚举项手动赋值,则会默认从 0 开始,第一项赋值为 0,第二项为 1,依次递增。当然也是可以手动赋值

ts
enum Color {
  red = 100,
  green = 200,
  blue = 300,
}
let c: number = Color[red] // c 为 100

let d: string = Color[200] // return 'green'

TODO: 常数枚举、静态枚举

  • never 永不存在的值的类型
  • object 对象类型,即为除基本类型之外的类型
  • 断言 当你清楚的知道一个值得类型时,可以使用断言来使 ts 跳过检查
ts
let someValue: any = "this is a string"
let strLength: number = (<string>someValue).length //  <>写法
let strLength: number = (someValue as string).length //  as写法

ts 在 es6 的类的基础上,又额外拓展了一些新的用法。

在 ts 可以使用三种访问修饰符,public、static、private、protected

  • public 修饰的属性和方法是共有的,在任何地方都可以访问到,默认所有的属性和方法都是 public
  • static 修饰的属性和方法都是通过类来访问,而不是通过类的实例来访问
  • private 修饰的属性和方法是私有的,只能在类的内部使用,不能在类的外部访问
  • protected 修饰的属性和方法也是私有的,与 private 不同的是,它在子类中是允许访问的
ts
class Person {
  public name
  constructor(name: string) {
    this.name = name
  }
  static num = 2
}

泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

ts
function createArray<T>(length: number, value: T): Array<T> {
  let result: T[] = []
  for (let i = 0; i < length; i++) {
    result[i] = value
  }
  return result
}

createArray<string>(3, "x")

上面函数声明的时候,没有先定义参数 value 的类型,而是用了 <T> 来指代任意输入的类型,在调用函数的时候,传入 <string> 来表明 T 指代的是 string 类型,也可以不显示传入 string 类型,让 ts 进行类型推论

类型断言

有时候你可能比 ts 更能确定一个变量的类型,可以使用类型断言,来欺骗 ts 解析器

ts
as 类型

接口

我们使用 interface 来定义对象的类型,接口一般首字母大写

ts
interface Person {
  name: String
  age: number
}

let tom:Person = {
  name: 'tom'
  age: 12
}

上面的例子中,我们定义了一个接口 Person,定义了一个变量 tom,并给它赋予了类型 Person,那么此时 tom 必须要拥有这两个属性,不能够再有其他属性,且这两个属性都得符合对应的类型

可选属性

可选属性的含义是这个属性可以不存在,如果存在的话,就必须符合这个类型

ts
interface Person {
  name: string
  age?: number
}

任意属性

如果我们希望在规定的属性之外,可以额外拓展其他属性的话,可以用 propName

ts
interface Person {
  name: string
  age?: number
  [propName: string]: string | number | undefined
}

注意:任意属性的类型必须包含其他属性的类型,因为接口有个可选属性 age,所以 任意属性的类型还得包含 undefined,一个常见的使用方法是使用 any 类型

只读属性

如果我们希望某个属性不能够再更改,那么可以使用 readonly

ts
interface Person {
  readonly id: number
  name: string
}

那么给变量赋予这个类型的时候,readonly 属性就不能够再更改了

ts
let p: Person = {
  id: 124,
  name: "xx",
}
p.id = 1 // 这是不行的