这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
学习函数类型
我们目前已经学习了类型推论、类型断言、联合类型、类型别名等知识。
没错,相信大家对 TS 的类型系统已经有了自己的了解。
类型系统不仅能够提高我们的开发效率,增强代码的可维护性。
而且在代码上线前的编译阶段就能提前发现一些错误。
下面我们就来继续学习一下 TS 中的函数类型吧。
函数的类型
接下来,我们学习下函数类型在 TS 中是如何定义的?
JS 代码
function getName(firstName, lastName) {
return firstName + ' ' + lastName;
}
TS 代码
我们知道一个函数分为输入和输出这两部分,当我们要定义函数类型,就是要把输入类型和输出类型都定义出来,接下来,我们看看这个简单的例子:
function getName(firstName: string, lastName: string) : string {
return firstName + ' ' + lastName;
}
// 使用函数,使用过程中,会给出代码提示,第一个参数、第二个参数以及返回值是什么类型
// 同时,这个变量会根据函数的返回值,推论出类型
let myName = getName('Alex', 'Zhang');
// 如果我们调用函数的时候,多传一个参数,就会报错,因为我们定义的时候,只定义了两个参数,这里却传了3个
// 同时,我们少传参数的话,也是会报错,报错信息:应有 2 个参数,但获得 1 个
// let myName = getName('Alex', 'Zhang', 'P');
但是有的时候,我们希望第二个参数是可选参数
function getName(firstName: string, lastName?: string) : string {
if (!lastName) {
return firstName;
}
return firstName + ' ' + lastName;
}
可选参数有第二种方式,在参数后面加上默认值
function getName(firstName: string, lastName?: string = 'Wu') : string {
if (!lastName) {
return firstName;
}
return firstName + ' ' + lastName;
}
习题-根据函数类型知识,完善下面代码块
type animalType = {
name: string;
weight: number;
};
const panda: animalType = {
name: 'panda',
weight: 10
};
const animal = [panda, panda];
// 尽量使用已声明的类型,可以提高代码阅读星
function foo(index: _____): _____ {
return animal[index];
}
foo(0);
答案
// 第一处空白答案
number
// 第二处空白答案
animalType
解析
第一处空白是函数的参数类型,依据代码
foo(0);
可以看出参数的类型应该是 number
类型。
第二处空白是函数的返回值类型,函数返回的是数组 animal
的 0
号元素。所以函数返回值类型为
{
name: string;
weight: number;
}
由于代码块中已经声明类型别名 animalType
,所以这里用 animalType
更合适,可以增加代码阅读性。
类型推论 - 函数
我们接着前面的例子来学习,类型推论在函数中的应用。
// 把函数的输入和输出的类型全部去掉,输入和输出都会被推论成 any,所以 myName 也是 any 类型
function getName(firstName, lastName?) {
if (!lastName) {
return firstName;
}
return firstName + ' ' + lastName;
}
let myName = getName('Alex');
// 保留输入的类型,但是不添加输出类型呢?输出被推论成 string,因为这个函数中每一个分支的输出,都是可以根据输入推论出来是一个 string 类型,所以输出是 string 类型
function getName(firstName: string, lastName?: string) {
if (!lastName) {
return firstName;
}
return firstName + ' ' + lastName;
}
let myName = getName('Alex');
// 如果我们不是给函数的输入添加类型,而是给参数输入添加默认值,这样的话,也可以正确的推论出输出的类型为 string
function getName(firstName = 'Bob', lastName = 'Wu') {
if (!lastName) {
return firstName;
}
return firstName + ' ' + lastName;
}
let myName = getName('Alex');
习题-严格类型模式下,变量 animal
的类型为?
const animal = function() {
return { name: 'panda', weight: 10 };
}
A. any
B. () => { name: string; weight: number }
C. { name: string; weight: number }
答案:B
解析
变量 animal
被赋值为函数,因此变量 animal
会被推论为具体的函数类型。
- A -
any
不是具体的函数类型。故错误。 - B - 本选项是一个函数类型,且函数的参数和返回值类型与赋值函数的参数和返回值类型相符。故正确。
- C - 本选项是一个对象类型(函数的返回值类型)。故错误。
资料-剩余参数
在 EcmaScript 6 中,可以 ...rest 的方式定义剩余参数(rest 参数)
function bookList(list: string[], ...books: string[]): string[] {
books.forEach(item => {
list.push(item);
});
return books;
};
let list = bookList([], 'book1', 'book2', 'book3');
从上述代码我们可以看出 ...books 实际上是一个数组,所以我们用 string[] 来定义了它的类型
注意:...rest 参数只能是最后一个参数。关于 rest 参数,可以参考 ES6 中的 rest 参数。
函数表达式 vs 函数声明
在 JS 有两种方式定义函数,就是函数声明和函数表达式。
// 函数声明
function getName(firstName, lastName) {
return firstName + lastName;
}
// 函数表达式
let getUsername = function (firstName, lastName) {
return firstName + lastName;
}
// 鼠标移动到上面,上面这个函数表达式,输入和输出都被推论成 any 类型
接下来,我们使用类型声明的方式,给它的输入和输出都定义为 string 类型,然后再将鼠标移动到上面,可以看到有了对应的类型推论
let getUsername = function (firstName: string, lastName: string): string {
return firstName + lastName;
}
// 在调用的过程中,编辑器就会提示输入的参数和输出是什么类型
我们是不是可以在定义变量的时候,不去定义后面的函数类型,而是直接给这个变量定义类型呢?
答案是可以的。
type GetUserNameFunc = (x: string, y: string) => string;
let getUsername: GetUserNameFunc = function (firstName, lastName) {
return firstName + lastName;
}
getUsername('Alex', 'Wu');
箭头在 ES6 中,用来表示一个函数,而在 TS 中,箭头表示一个函数的类型
习题-严格类型模式下,变量 getAnimal
类型为
const animal = [{ name: 'panda', weight: 10 }];
type AnimalType = { name: string; weight: number; }
const getAnimal = function(index: number): AnimalType {
return animal[index];
}
A. AnimalType
B. (index: number) => AnimalType
C. (index: number) => number | string
D. number | string
答案:B
解析
变量 getAnimal
被赋值为函数,因此变量 getAnimal
会被推论为具体的函数类型。
- A - 本选项的
animalType
类型是定义的类型别名,是一个对象类型。故错误。 - B - 本选项是一个函数类型,且函数的参数和返回值结构类型,与赋值函数的参数和返回值结构类型相符。故正确。
- C - 本选项是一个函数类型,但本选项中函数的返回值类型是一个联合类型,联合类型中均为基本数据类型。与赋值函数的返回值类型不符合。故错误。
- D - 本选项的联合类型中均为基本数据类型。故错误。
箭头函数
下面我们来看看箭头函数类型的定义。
定义一个箭头函数很简单,就是把普通函数的 function 去掉,后面加上箭头即可。
需要区分 TS 中的函数类型定义。我们这里是定义了一个真正的函数,而不是一个类型。
跟上面的函数类型的使用差不多。
let getUsername = (firstName: string, lastName: string): string => {
return firstName + lastName;
}