Skip to content
索引

基础语法

类型注解

这是TypeScript最基础的语法,为变量指明类型,称为类型注解,语法为:变量:类型

ts
let isDone: boolean = false
let isDone: boolean = false

基础类型

原始数据类型

JavaScript分为原始数据类型和对象类型,原始数据类型有:numberstringbooleannullundefined

TypeScript中,相应类型注解为:

ts
let tsNum: number = 123
let tsStr: string = 'abc'
let tsFlag: boolean = true
let tsNull: null = null
let tsUndefined: undefined = undefined
let tsNum: number = 123
let tsStr: string = 'abc'
let tsFlag: boolean = true
let tsNull: null = null
let tsUndefined: undefined = undefined

null与undefined

TypeScript中,nullundefined是所有类型的子类型,也就是说可以把undefinednull赋值给原始数据类型的变量

ts
let tsNumber1: number = undefined
let tsNumber2: number = null
let tsNumber1: number = undefined
let tsNumber2: number = null

void

JavaScript中,是没有空值(void)的概念的,但在TypeScript中,可以使用void来表示一个没有返回值的函数

ts
function sayHello (): void {
  console.log('Hello, world')
}
function sayHello (): void {
  console.log('Hello, world')
}

也可以定义一个void类型的变量,不过这样的变量并没有什么意义,因为只能给这种变量赋值为nullundefined

这也是voidnullundefined的区别,对于void而言,它只能被赋值为null或者undefined

ts
let voidValue1: void = null
let voidValue2: void = undefined
// 报错
let voidValue1: void = 123
let voidValue2: void = 'abc'
let voidValue1: void = null
let voidValue2: void = undefined
// 报错
let voidValue1: void = 123
let voidValue2: void = 'abc'

any

任意值any用来表示可以接受任何类型的值

以下代码会报错,因为变量被定义为number,那么它只能接受number类型的值,改变其类型,会编译报错

ts
let tsNumber: number = 123
tsNumber = '123'
let tsNumber: number = 123
tsNumber = '123'

但是如果一个变量被定义为any,那么代表它可以接受任何类型的值:

ts
// 类型正确
let tsAny: any = 123
tsAny = '123'
// 类型正确
let tsAny: any = 123
tsAny = '123'

如果定义了一个变量,没有指定其类型,也没有初始值,那么它默认为any类型

ts
// 类型正确,不会报错,因为默认为any类型
let tsValue
tsValue = 123
tsValue = '123'
// 类型正确,不会报错,因为默认为any类型
let tsValue
tsValue = 123
tsValue = '123'

接口

接口interface用于为对象提供类型注解, 接口interface中的任何代码都不会被最后编译到JavaScript

ts
interface Person {
  name: string,
  age: number
}
let person: Person = {
  name: 'why',
  age: 23
}
interface Person {
  name: string,
  age: number
}
let person: Person = {
  name: 'why',
  age: 23
}

在上方代码中,person对象的类型是接口Person,那么person对象的所有属性只能是Person规定的属性,且属性值的类型也必须和Person中规定的一致,多一个属性或者少一个属性都会报错

ts
interface Person {
  name: string,
  age: number
}
// 报错
let person1: Person = {
  name: 'why'
}
// 报错
let person2: Person = {
  name: 'why',
  age: 23,
  sex: 'man'
}
interface Person {
  name: string,
  age: number
}
// 报错
let person1: Person = {
  name: 'why'
}
// 报错
let person2: Person = {
  name: 'why',
  age: 23,
  sex: 'man'
}

接口中的任意属性

以上一个例子为基础,假设接口只对nameage做规定,其它任何属性名与属性类型都是可以的,那么可以如下方式进行定义:

ts
interface Person {
  name: string,
  age: number,
  // 任意属性名与属性类型
  [propName: string]: any
}
// 类型正确
let person: Person = {
  name: 'why',
  age: 23,
  sex: 'man'
}
interface Person {
  name: string,
  age: number,
  // 任意属性名与属性类型
  [propName: string]: any
}
// 类型正确
let person: Person = {
  name: 'why',
  age: 23,
  sex: 'man'
}

接口中的可选属性

?:可选属性,代表该属性可存在可不存在,但存在的话类型必须无误

ts
interface Person {
  name: string,
  age?: number // age属性是可选的
}
// 类型正确
let person1: Person = {
  name: 'why'
}
// 报错
let person2: Person = {
  name: 'why',
  age: "123"
}
interface Person {
  name: string,
  age?: number // age属性是可选的
}
// 类型正确
let person1: Person = {
  name: 'why'
}
// 报错
let person2: Person = {
  name: 'why',
  age: "123"
}

接口中的只读属性

readonly表示属性只读,不可修改,在接口中标记了属性为只读的, 那么其不能被赋值。

ts
interface Person {
  name: string,
  readonly age: number
}
let person: Person = {
  name: 'why',
  age: 23
}
// 报错
person.age = 32
interface Person {
  name: string,
  readonly age: number
}
let person: Person = {
  name: 'why',
  age: 23
}
// 报错
person.age = 32

函数类型

为参数和返回值设置类型,不定义相应类型的话,默认为any

ts
/**
 * @param x 数字1
 * @param y 数字2
 * @returns 数字1和数字2之和
 */
function add (x: number, y: number): number {
  return x + y
}
console.log(add(1, 2))    // 输出3
console.log(add(1, '2'))  // 报错
/**
 * @param x 数字1
 * @param y 数字2
 * @returns 数字1和数字2之和
 */
function add (x: number, y: number): number {
  return x + y
}
console.log(add(1, 2))    // 输出3
console.log(add(1, '2'))  // 报错

也可以使用interface为函数设置类型

ts
interface AddInterface {
  (x: number, y: number): number
}
/**
 * @param x 数字1
 * @param y 数字2
 * @returns 数字1和数字2之和
 */
const add: AddInterface = (x, y) => {
  return x + y
}
console.log(add(1, 2)) // 输出3
console.log(add(1, '2'))  // 报错
interface AddInterface {
  (x: number, y: number): number
}
/**
 * @param x 数字1
 * @param y 数字2
 * @returns 数字1和数字2之和
 */
const add: AddInterface = (x, y) => {
  return x + y
}
console.log(add(1, 2)) // 输出3
console.log(add(1, '2'))  // 报错

可选参数

函数接受可选的参数,使用?:

ts
function getArea (a: number, b?: number): number {
  return  b ? a * b : a * a
}
console.log(getArea(4))     // 16
console.log(getArea(4, 5))  // 20
function getArea (a: number, b?: number): number {
  return  b ? a * b : a * a
}
console.log(getArea(4))     // 16
console.log(getArea(4, 5))  // 20

WARNING

可选参数必须放在最后一个位置,否则会报错

ts
// 报错
function getArea (b?: number, a: number): number {
  return  b ? a * b : a * a
}
// 报错
function getArea (b?: number, a: number): number {
  return  b ? a * b : a * a
}

参数默认值

JavaScript中,函数允许给参数设置默认值,因此另外一种处理可选参数的方式是,为参数提供一个默认值,此时TypeScript将会把该参数识别为可选参数:

ts
function getArea (a: number, b: number = 1): number {
  return  a * b
}
console.log(getArea(4))     // 4
console.log(getArea(4, 5))  // 20
function getArea (a: number, b: number = 1): number {
  return  a * b
}
console.log(getArea(4))     // 4
console.log(getArea(4, 5))  // 20

TIP

给一个参数设置了默认值后,就不再受TypeScript可选参数必须在最后一个位置的限制了

ts
function getArea (b: number = 1, a: number): number {
  return  a * b
}
// 传递一个undefined占位,undefined不影响参数1的默认值,仅作占位用
console.log(getArea(undefined,4)) // 4
console.log(getArea(4, 5))        // 20
function getArea (b: number = 1, a: number): number {
  return  a * b
}
// 传递一个undefined占位,undefined不影响参数1的默认值,仅作占位用
console.log(getArea(undefined,4)) // 4
console.log(getArea(4, 5))        // 20

剩余参数

ES6中,可以使用...符号进行传递剩余参数,在TypeScript中,依然可以这么做

ts
// rest是一个数组,可以使用数组的类型来定义它
function getTotal (a: number, ...rest: number[]) {
  console.log(a)    // 1
  console.log(rest) // [2, 3, 4]
}

getTotal(1, 2, 3, 4)
// rest是一个数组,可以使用数组的类型来定义它
function getTotal (a: number, ...rest: number[]) {
  console.log(a)    // 1
  console.log(rest) // [2, 3, 4]
}

getTotal(1, 2, 3, 4)

函数重载

因为在JavaScript中,并没有限制函数参数的个数或者类型,因此JavaScript没有函数重载的概念

TypeScript中对于函数重载的理解是:只要函数参数的类型或者函数参数的数量不同时,就可以认为这是两个函数(重载)

下方示例,前三个都是重载,最后一个才是实现,因为实现只包含了前两个重载,所以第三个重载会报错

ts
function add(a: number, b: number): number
function add(a: string, b: string): string
// 报错,此重载签名与其实现签名不兼容
function add(a: boolean, b: boolean): boolean
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
function add(a: number, b: number): number
function add(a: string, b: string): string
// 报错,此重载签名与其实现签名不兼容
function add(a: boolean, b: boolean): boolean
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

在有函数重载时,会优先从第一个进行逐一匹配,因此如果重载函数有包含关系,应该将最精准的函数定义写在最前面

数组类型

两种方法定义数组类型

方法一 数组泛型,Array<type>

ts
let list: Array<number> = [1, 2, 3]
let list: Array<number> = [1, 2, 3]

方法二 type[ ]

ts
let list: number[] = [1, 2, 3]
let list: number[] = [1, 2, 3]

联合类型数组

ts
let tsArray: (number | string) [] = ["1", '2', 3]
let tsArray: (number | string) [] = ["1", '2', 3]

对象类型数组

ts
let objArray: { name: string; age: number }[] = [{ name: "AAA", age: 23 }]
let objArray1: Array<{name: string; age: number}> = [{name: "AAA", age: 23}]
interface obj {
  name: string
  age: number
}
let objArray2: obj[] = [{ name: "AAA", age: 23 }]
let objArray3: Array<obj> = [{ name: "AAA", age: 23 }]
let objArray: { name: string; age: number }[] = [{ name: "AAA", age: 23 }]
let objArray1: Array<{name: string; age: number}> = [{name: "AAA", age: 23}]
interface obj {
  name: string
  age: number
}
let objArray2: obj[] = [{ name: "AAA", age: 23 }]
let objArray3: Array<obj> = [{ name: "AAA", age: 23 }]

元组

一个数组的长度是确定的,且每个位置的值的类型也是确定的,那么就可以把这样的数组称为元组

ts
let nameNumber: [string, number] = ['1',2]
let nameNumber: [string, number] = ['1',2]

当访问元组中已知位置的索引时,将得到其对应正确的值;当访问元组中未知位置的索引时,会报错

ts
let tuple: [string, number] = ['AAA', 123]
console.log(tuple[1]) // 123
console.log(tuple[2]) // 报错
let tuple: [string, number] = ['AAA', 123]
console.log(tuple[1]) // 123
console.log(tuple[2]) // 报错

类型推断

在以上的所有实例中,为每一个变量提供了相应的类型,也就是类型注解。而有些时候,没有为其提供一个确定的类型,但提供了一个确定的值,那么TypeScript会根据给定的值的类型自动推断出这个变量的类型,称为类型推断

ts
let tsNum = 123 // 类型推断为number
let tsStr = "abc" // 类型推断为string
let tsFlag = true // 类型推断为boolean
let tsNull = null // 类型推断为any
let tsUndefined = undefined // 类型推断为any
const helloWorld = {
  hello: 123,
  world: "abc"
}
// 类型推断helloWorld的类型为
// const helloWorld = {
// hello: number,
// world: string
// }
let tsNum = 123 // 类型推断为number
let tsStr = "abc" // 类型推断为string
let tsFlag = true // 类型推断为boolean
let tsNull = null // 类型推断为any
let tsUndefined = undefined // 类型推断为any
const helloWorld = {
  hello: 123,
  world: "abc"
}
// 类型推断helloWorld的类型为
// const helloWorld = {
// hello: number,
// world: string
// }

由上方例子可见,null和undefined不能推断

另外,函数的参数无法推断,必须指定,但返回值可以推断,但建议写上,更加清晰

ts
function add (num1: number, num2: number): number {
  return num1 + num2
}
// 可以省略函数的返回值类型,因为typescript会基于num1和num1全部为number类型,从而推断出函数返回值为number类型
function add (num1: number, num2: number) {
  return num1 + num2
}
function add (num1: number, num2: number): number {
  return num1 + num2
}
// 可以省略函数的返回值类型,因为typescript会基于num1和num1全部为number类型,从而推断出函数返回值为number类型
function add (num1: number, num2: number) {
  return num1 + num2
}

联合类型

表示取值可以为多种类型中的一种,多种类型使用|分隔开

ts
let value: string | number
value = 123
value = '123'
let value: string | number
value = 123
value = '123'

当使用联合类型的时候,因为TypeScript不确定到底是哪一个类型,所以TypeScript只允许此联合类型中所有类型公用的属性和方法

ts
// length是string类型独有的属性,不是公用的属性,所以会报错
function getLength (value: string | number): number {
  return value.length
}

// toString是string类型和number类型公用的方法,不会报错
function valueToStr (value: string | number): string {
  return value.toString()
}
// length是string类型独有的属性,不是公用的属性,所以会报错
function getLength (value: string | number): number {
  return value.length
}

// toString是string类型和number类型公用的方法,不会报错
function valueToStr (value: string | number): string {
  return value.toString()
}

当联合类型被赋值后,TypeScript会根据类型推断来确定变量的类型,一旦确定后,则此变量只能使用这种类型的属性和方法

ts
let tsValue: string | number
tsValue = '123'
console.log(tsValue.length) // 类型正确
tsValue = 123
console.log(tsValue.length) // 报错
let tsValue: string | number
tsValue = '123'
console.log(tsValue.length) // 类型正确
tsValue = 123
console.log(tsValue.length) // 报错

交叉类型

一般适用于对象或者函数的"合并",使用&符号进行连接

ts
type result = T & U
type result = T & U

T & U表示一个新的类型,其中这个类型包含TU中所有的键,这和JavaScript中的Object.assign()函数的作用非常类似

ts
function merge<T, U>(to: T, from: U): T & U {
  for (let key in from) {
    ;(to as T & U)[key] = from[key] as any
  }
  return to as T & U
}

const obj1 = { str: "abc" }
const obj2 = { num: 123 }
const result = merge(obj1, obj2)
function merge<T, U>(to: T, from: U): T & U {
  for (let key in from) {
    ;(to as T & U)[key] = from[key] as any
  }
  return to as T & U
}

const obj1 = { str: "abc" }
const obj2 = { num: 123 }
const result = merge(obj1, obj2)

类型断言

有些情况下typescript没办法自动推断出正确的类型, 就需要手动告诉typescript这个类型

断言有2种语法, 一种是通过<>, 一种通过as

ts
// ts推断类型为number|number[],实际上是number[]类型
let obj = 0.5 < 0.2 ? 0 : [0] 
// 报错,类型“number | number[]”上不存在属性“push”。类型“number”上不存在属性“push”
obj.push(1)
// 告诉ts, obj为数字数组类型
(obj as number[]).push(2) 
// 告诉ts, obj为数字数组类型
(<number[]>obj).push(3) 
// ts推断类型为number|number[],实际上是number[]类型
let obj = 0.5 < 0.2 ? 0 : [0] 
// 报错,类型“number | number[]”上不存在属性“push”。类型“number”上不存在属性“push”
obj.push(1)
// 告诉ts, obj为数字数组类型
(obj as number[]).push(2) 
// 告诉ts, obj为数字数组类型
(<number[]>obj).push(3) 

类型别名

type关键字来给一个类型起一个新的名字,类型别名常用于联合类型

ts
type Add = (x: number, y: number) => number
const add: Add = (a, b) => a + b
add(1,2) // 类型正确
add('1',2) // 报错,类型“string”的参数不能赋给类型“number”的参数。ts(2345)
type Add = (x: number, y: number) => number
const add: Add = (a, b) => a + b
add(1,2) // 类型正确
add('1',2) // 报错,类型“string”的参数不能赋给类型“number”的参数。ts(2345)

与其他类型组合

ts
interface A {
    a: number
}
type B = A | {b: string}
type C = A & {b: string}
const aa: B = {
    a: 1
}
const bb: B = {
    b: "1"
}
const cc: C = {
    a: 1,
    b:'1'
}
// 与泛型组合
type D<T> = A | T[]
interface A {
    a: number
}
type B = A | {b: string}
type C = A & {b: string}
const aa: B = {
    a: 1
}
const bb: B = {
    b: "1"
}
const cc: C = {
    a: 1,
    b:'1'
}
// 与泛型组合
type D<T> = A | T[]

类型别名和接口的区别

type和interface都可以定义对象类型

区别在于:

  • type可以定义interface不能定义的类型,如联合类型,字面量类型
  • type还可以与其他类型组合
  • interface可以继承、声明合并

字面量类型

字面量类型用来表示一个变量只能取某几个字符串值中的一个

ts
type eventName = 'click' | 'scroll' | 'mousemove'
function handleEvent (event: eventName) {
  console.log(event)
}
handleEvent('click')    
handleEvent('scroll')  
handleEvent('dbclick')  // 报错
type eventName = 'click' | 'scroll' | 'mousemove'
function handleEvent (event: eventName) {
  console.log(event)
}
handleEvent('click')    
handleEvent('scroll')  
handleEvent('dbclick')  // 报错

枚举

枚举Enum类型用来表示取值限定在指定的范围,例如一周只能有七天,颜色只能有红、绿、蓝等

ts
enum colors  {
  red,
  green,
  blue
}
console.log(colors.red)   // 0
console.log(colors.green) // 1
console.log(colors.blue)  // 2
enum colors  {
  red,
  green,
  blue
}
console.log(colors.red)   // 0
console.log(colors.green) // 1
console.log(colors.blue)  // 2

定义一个colors的枚举类型,其取值只能是redgreenblue。从打印的内容中发现,其输出值从0开始,依次累加1。这是枚举类型的默认行为,可以手动设置一个起始值

ts
enum colors  {
  red = 10,
  green,
  blue
}
console.log(colors.red)   // 10
console.log(colors.green) // 11
console.log(colors.blue)  // 12
enum colors  {
  red = 10,
  green,
  blue
}
console.log(colors.red)   // 10
console.log(colors.green) // 11
console.log(colors.blue)  // 12

在枚举类型中,不仅可以正向的获取值,还可以通过值反向获取枚举

ts
enum colors  {
  red = 10,
  green,
  blue
}
console.log(colors[10]) // red
console.log(colors[11]) // green
console.log(colors[12]) // blue
enum colors  {
  red = 10,
  green,
  blue
}
console.log(colors[10]) // red
console.log(colors[11]) // green
console.log(colors[12]) // blue

泛型

根据传入的参数自动判断类型

ts
function a<T>(arr:T[]):T[] {
    return arr
}
let b = a([1,2,3])
b = [1] // 类型正确
b = ['1'] // 报错,不能将类型“string”分配给类型“number”
function a<T>(arr:T[]):T[] {
    return arr
}
let b = a([1,2,3])
b = [1] // 类型正确
b = ['1'] // 报错,不能将类型“string”分配给类型“number”

Released under the MIT License.