重新TS --- Function

268 阅读6分钟

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

函数是JavaScript应用程序的基础,是JavaScript的一等公民

在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义_行为_的地方。

TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。

基本属性

具名函数定义

/**
 * 在TS中,使用function关键字来定义一个最基本的函数
 * @param num1 求和的第一个值 类型为 number
 * @param num2 求和的第二个值 类型为 number
 * @returns 返回两数之和 类型为number --- 返回值的类型 一般可以不写 ts的类型推断会自动推测出来
 */
function add(num1: number, num2: number): number {
  return num1 + num2
}

匿名函数定义

/**
 *
 * @param {number} num1
 * @param {number} num2
 * @returns {number}
 */
const add = (num1: number, num2: number) => num1 + num2
// 变量为函数类型
let add: (num1: number, num2: number) => number

add = (num1: number, num2: number): number => num1 + num2

如果一个函数中,使用了在函数外部定义的变量,那么该变量的类型是不会体现在这个函数中的

而当函数这么做时,我们说它‘捕获’了这些变量。实际上,这些捕获变量是函数的隐藏状态并不是组成API的一部分。

const num = 33

// 定义类型别名
type Add = (num1: number, num2: number) => number

// 这里的num是定义在函数add之外的变量
// 所以num的数据类型是不会体现在add函数的ts类型结构中的
const add: Add = (num1: number, num2: number) => num1 + num2 + num

完整定义

函数类型包含两部分:参数类型和返回值类型

(num1: number, num2: number) => number

函数参数

  • 我们以参数列表的形式写出参数类型,为每个参数指定一个名字和类型。

  • 这个名字只是为了增加可读性, 并不需要和形参名保持一致

  • TypeScript只要求对应位置上的参数的参数类型是匹配的,那么就认为它是有效的函数类型,而不在乎参数名是否正确

函数返回值

  • 在函数和返回值类型之前使用(=>)符号 来进行分割,=>右边即为函数的返回值
  • 返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为void而不能留空。

类型推测

// 写法1
const add: (num1: number, num2: number) => number =
    function(num1, num2) { return num1 + num2; };

// 写法2
const add = function(num1: number, num2: number): number { return num1 + num2; };

可选参数

JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined

在TypeScript里我们可以在参数名旁使用?实现可选参数的功能

// 和JS不一样的是
// 在JS中,可选参数可以出现在形参列表中的任意位置,如果不是最后一个,在实际传参的时候,使用undefined进行占位即可
// 在TS中,可选参数 必须 出现在必选参数后边
type Add = (num1: number, num2: number, num3?: number) => number

let add: Add
add = (num1: number, num2: number) => num1 + num2
add = (num1: number, num2: number, num3: number) => num1 + num2 + num3

默认参数

在TypeScript里,我们也可以为参数在用户没有传递这个参数或传递的值是undefined时提供一个默认值

// 默认参数的写法和js是一致的
// 默认参数是定义在函数实际定义中 --- 默认参数是不可以定义在接口或类型别名中的
// --- 在接口或类型别名中对应的是可选参数
let sum = (num1: number, num2: number = 2) => num1 + num2

// num2:number = 2 --- 已经为num2赋值了默认值2,ts会自动推测出num2的数据类型是number
// 所以num2的数据类型默认可以省略
sum = (num1: number, num2 = 2) => num1 + num2
let sum = (num1: number, num2: number = 2) => num1 + num2

// 和 

sum = (num1: number, num2?: number) => num1 + num2

// 在TS中所对应的类型是一致的
// 都是 (num1: number, num2?: number) => number
// 也就是在函数类型中,默认参数的默认值不会显示,而只会显示它是一个可选参数
// 但与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面
// 如果带默认值的参数出现在必须参数前面,用户必须明确的传入undefined值来获得默认值

剩余参数

必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用arguments剩余参数来访问所有传入的参数。

剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个

编译器创建参数数组,名字是你在省略号(...)后面给定的名字,你可以在函数体内使用这个数组。

function foo(...args: number[]) {
  console.log(args.length)
}

函数重载

JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。

// 函数重载定义部分
// 函数重载只能使用function关键字
// 不可以使用接口或类型别名来定义函数重载

function getArr(target: number): string[]
function getArr(target: string): string[]

// 函数具体实现部分
// 单单存在函数重载部分,ts是会报错的
// 函数重载必须存在一个具体的函数实现

// TS会自动根据函数的实现和函数重载的定义
// 去自动从上往下 去使用匹配到的第一个函数重载定义
// 所以如果存在多个可以匹配到的函数重载定义
// 需要将最为精确的哪一个函数重载定义放置到最上边
function getArr(target: number | string) {
  if (typeof target === 'number') {
    target = String(target)
  }

  return target.split('')
}

console.log(getArr('Klaus'))
console.log(getArr(23))

函数中的this

JavaScript里,this的值在函数被调用的时候才会指定。 这是个既强大又灵活的特点

但是,这也就导致TS在编译阶段对代码进行类型检测的时候,可能无法准确推测出this所对应的类型结构

interface IPerson {
  name: string,
  sayHello(): void
}

const person: IPerson = {
  name: 'Klaus',

  sayHello () {
    console.log(this.name)
  }
}

person.sayHello()

此时,编辑器并不知道sayHello是如何调用的,所以此时的this在经过TS的类型推测后,其类型为any

interface IPerson {
  name: string,
  sayHello(): void
}

const person: IPerson = {
  name: 'Klaus',

  // 我们可以在函数调用的时候传入一个参数this来告诉编辑器,我们会使用什么方式来调用对应的方法
  // this参数是个假的参数,它总是出现在参数列表的最前面
  // 如果我们不希望函数内部使用this的时候,可以显示设置this的值为void
  sayHello (this: IPerson) {
    console.log(this.name)
  }
}

person.sayHello()