首页 前端知识 Typescript 函数类型详解

Typescript 函数类型详解

2024-06-07 12:06:08 前端知识 前端哥 246 504 我要收藏

Typescript 函数

前言

虽然 JS/TS 支持面向对象编程,但大部分时候还是在写函数。函数是一等公民。本文介绍下如何在 TypeScript 中使用函数,包括:

  • 函数类型声明
  • 函数参数类型:可选参数、默认参数、剩余参数
  • 函数返回值类型
  • this 类型
  • 函数重载

函数类型

面试中经常会被问到,JS 中有哪几种数据类型。其中就会有函数类型。

JS 中的函数类型很模糊,准确来说,仅有类型的概念,却无类型的实质。好在有了 TS 强类型的加持,现在可以好好定义一个函数的类型了。

声明一个函数类型,有若干种不同的方式。比如通过 interface 定义函数类型,通过 type 声明函数类型别名,在类声明中声明函数类型等等。

但核心就一点,只关注函数参数的类型返回值的类型

下面就从函数的声明入手,来看看函数类型的使用。

函数的声明方式

有三种最简单的函数声明方式。

使用 function 关键字进行声明:

function add(a: number, b: number): number {
  return a + b
}

通过函数表达式进行声明:

let add = function (a: number, b: number): number {
  return a + b
}

或者使用箭头函数:

let add =  (a: number, b: number): number => {  
    return a + b
}

上面这三种声明函数的方式和 JS 中声明函数别无二致,就是多了两点:

  • 函数参数需要给明类型
  • 函数返回值也需要给出类型

那么函数类型到底在哪里呢?把鼠标移动到函数名字上就能看到了:

image-20221114151636908

image-20221114151710177

image-20221114152126540

和声明普通变量变量一样:

let name = 'kunwu'

如果没有使用类型注解,那么编译器会自动推导出类型来。上面红框标识出来的类型,就是编译器推导出来的。

函数的类型注解

函数的类型形如 (参数:类型) => 类型,所以函数的类型注解就是:

let add: (a: number, b: number) => number = function (a, b) {
  return a + b
}

通常函数的类型都比较长,此时可以使用 type 类型别名,来声明一个标识符表示该类型:

type Add = (a: number, b: number) => number

let add: Add => number = function (a, b) {
  return a + b
}

这样,声明函数类型,使用函数类型,看上去就很简洁直观了。

对象中的函数类型

函数的参数

声明函数时,可以使用类型注解声明参数类型。如果不指定类型,默认类型是 any

function add (a: number, b: number) :number {
    return a + b
}

image-20221115004807260

可选参数

可选参数使用 ? 标记,需要放到固定参数的后面:

const fn = (a: number, b?:number) => {
    if(b) {
        return a + b
    } else {
        return a
    }
}

可选参数意味着参数可传可不传。通过编译器的类型推导,可以看到好像可选参数等同于一个联合类型:

image-20221115004923924

但其实并不等同。

可选参数表示参数可传可不传,若传则必须是指定的类型。

而直接将参数类型声明为 string|undefined,意味着这是个必传参数,必须传实参。

参数默认值

参数的默认值,在参数的类型后面使用 = 声明:

const fn = (a: number, b: number = 10): number => {
    return a + b
}

剩余参数

剩余参数是 ES6 中的一个特性,使用剩余运算符 ... 来获取函数实参中未被形参声明的变量,其取得的值是一个数组,比如:

function add(a,b, ...args) {
    console.log(args)
}

add(1,2,3,4,5)

则剩余参数 args 就等于 [3, 4, 5]。

TS 中剩余参数也要有类型声明,需要将其声明为数组类型或者元组类型。

function add(a: number, b: number, ...args: number[]) {
    console.log(c)
}

剩余参数和其他的固定参数不同。其他的固定参数声明了类型,则必须传该类型的值。而剩余参数虽然也声明了类型,但可传可不传,可传一个,可传多个,比如:

function add(a: number, b: number, ...args: number[]) {
    console.log(args)
}

add(1, 2) // [ ]
add(1, 2, 3) // [ 3 ]
add(1, 2, 3, 4) // [ 3, 4 ]

还可以将剩余参数类型声明为元组类型,元组中还可以继续使用可选参数,在参数后使用 ? 表示:

function log( ...args: [string, number, boolean?]) {
    console.log(args)
}

log('Shinichi', 17) // [ 'Shinichi', 17 ]
log('Zoro', 19, true) // [ 'Zoro', 19, true ]

函数的返回值类型

函数的返回值类型可以通过类型注解指定。如果不指定的话,TS 编译器能够根据函数体的 return 语句自动推断出返回值类型,因此我们也可以省略返回值类型。

TS 基本类型中有一个 void 类型,表示空类型,它唯一的用处就是用作函数的返回值类型。当一个函数没有 return 语句时,

image-20221115125205523

如果函数使用 return 语句返回了 undefined 值,则返回值类型就为 undefined 而不是 void 了:

image-20221115125323627

但是此时可以将返回值类型使用类型注解指定为 void:

image-20221115125425635

this 类型

JS 函数中到处可见 this 的身影。关于 this 的指向也是前端八股中的基础之基础。

默认情况下 TS 编译器会将函数中的 this 设为 any 类型,也就是说编译器不会对 this 做类型检查,就可以任意使用 this,比如:

function fn() {
    this.name = 'Naruto'
    this.age = 18
}

同时 TS 编译器又提供了一个编译选项 --noImplicitThis,开启后会检查 this 的类型,此时需要明确指定其类型,否则会报错,如下:

image-20221115150823423

那么如何为 this 声明类型呢?

声明 this 类型

TS 函数中有一个特殊的 this 参数,它用来指定该函数中用到的 this 的类型,需要定义在形参的第一个位置。

还是上面的例子,要为函数中的 this 指定类型的话,这样写:

function fn(this: {name: string, age: number}) { 
  this.name = 'Naruto'
  this.xxx = 'xxx'
}

直接在函数参数列表中声明 this 类型不太优雅,可以使用 type 关键字声明别名再使用:

type Person = {name: string, age: number}

function fn(this: Person) { 
  this.name = 'Naruto'
  this.age = 18
}

当定义了 this 类型后,函数在调用时也会有所不同,需要使用 call、apply :

type Person = {name: string, age: number}

function fn(this: Person, a: number) { 
  this.name = 'Naruto'
  this.age = 18
}


fn.call({name: 'Naruto', age: 18}, 10)

fn.apply({name: 'Naruto', age: 18}, [10])

像以前那样直接调用函数是错误的:

fn(10) // X

函数重载

面向对象编程有三大特征,封装、继承和多态。多态的表现之一就是函数的重载。

函数重载,就是可以多次声明一个同名函数,但是它们的参数类型不同或者参数个数不同。这样在调用时,可以根据传入参数类型的不同,参数个数的不同,来确定执行的到底是哪一个函数。

函数重载是后端语言的概念,比如 java 中:

// 两个整数的和
public static int add(int a, int b)  {
  return a+b;
}

// 三个整数的和
public static int add(int a, int b, int c) {
 return add(a,b)+c;
}

JS 本身并不支持函数重载。如果多次声明一个函数,则后声明的会覆盖掉先声明的。但是 JS 可以利用 arguments 对象,再加上判断实参的类型,来模拟重载的功能,比如:

function add() {
   let args = Array.from(arguments)
   if(args.length == 2) {
       return args[0] + args[1]
   } else if(args.length == 3) {
        return args[0] + args[1] + args[3]
   }
}

add(1,2)
add(1, 2, 3)

TS 中多次声明一个同名函数,编译器会报错。但是 TS 提供了实现函数重载的方法:先通过 function 关键字声明重载的类型,最后写函数的实现。

function add(a: number, b: number) : number

function add(a: string, b: string) : string

function add(a: number|string, b: number | string) {
  if(typeof a === 'number' && typeof b === 'number') {
    return a + b
  }  else if(typeof a === 'string' && typeof b === 'string') {
    return a + b
  } 
}

add(1, 2)
add('10', '20')

由于在声明重载时已经确定了函数的返回值类型,在写函数实现时,就不再需要写返回值类型了。编译器会根据重载类型自动推导。

TS 的函数重载只是一种伪重载,最终还是要靠去判断类型,来执行不同的逻辑。

还有一点要注意,声明函数重载和函数实现必须写在一块,中间不能插入其他语句。

总结

本文介绍了TS 中有关函数的知识,包括函数的声明方式,如何声明函数类型,函数参数和返回值的类型,函数重载以及 this 的类型。大部分内容和 JS 中差不太多,主要是 this 类型和函数重载这两点,需要额外关注下。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/11238.html
标签
评论
发布的文章

1.10 Unity中的数据存储 JSON

2024-06-13 21:06:30

JSON 数据格式化方法

2024-06-13 21:06:26

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!