函数的参数与返回值
参数与返回值是函数的重要组成部分,它们将决定函数的调用形式。在 TypeScript 中,你可以定义多种参数,并对返回值进行约束。接下来,将详细介绍。
普通参数与类型推导
在 TypeScript 中,通过 “参数名称:参数类型” 的方式为函数指明参数。如果传入的参数值不符合函数参数声明的类型,则会引起编译错误,示例代码如下。
function sum(num1: number, num2: number): number {
return num1 + num2;
}
//编译错误:类型"string"的参数不能赋给类型"number"的参数。ts(2345)
let num3: number = sum("a", "b");
在函数声明时,若省略参数类型,参数类型将默认为 any
类型(任意类型)。示例代码如下。
//编译错误:num1和num2隐式具有 "any"类型,但可以从用法中推断出更好的类型。ts(7044)
function sum(num1, num2): number {
return num1 + num2;
}
let num3 = sum(1, 2);
//由于参数已经变为any类型,因此以下代码中传入的字符串不会引起编译错误
let num4 = sum("a", "b");
|
在 Visual Studio Code 中,如果不确定一个函数的参数类型,通过以下步骤进行自动推导。
-
将光标移动到参数上,会出现 “快速修复” 选项,选择 “快速修复” 选项,如图8-1所示。
Figure 1. 图8-1 选择“快速修复”选项 -
选择 “从使用情况推导所有类型” 选项,如图8-2所示。
Figure 2. 图8-2 选择“从使用情况推导所有类型”选项 -
Visual Studio Code 会自动为函数的参数加上类型声明,如图8-3所示。
Figure 3. 图8-3 自动为函数的参数加上类型声明
可选参数
默认情况下,函数声明中定义了多少个参数,在调用时就需要传入多少个,而且类型必须符合参数定义,否则会引起编译错误,示例代码如下。
function sum(num1: number, num2: number): number {
return num1 + num2;
}
//编译错误:应有2个参数,但获得1个。ts(2554)
let num3 = sum(1);
//编译错误:应有2个参数,但获得3个。ts(2554)
let num4 = sum(1,2,3);
然而,在一些情况下,某些参数可能并不是必需的,因此就需要定义可选参数。在 TypeScript 中,通过在参数后面添加问号将参数定义为可选参数。在调用函数时,可选参数的值可传可不传,示例代码如下。
function sum(num1: number, num2?: number, num3?: number): number
{
let result = num1;
if (num2) result += num2; // 判空处理
if (num3) result += num3; // 判空处理
return result;
}
let num3 = sum(1); //值为1
let num4 = sum(1, 2); //值为3
let num5 = sum(1, 2, 3); //值为6
若未向可选参数传递值,则参数值默认为 undefined
,示例代码如下。建议做判空处理,只有当可选参数有值时才进行处理。在上一个示例中,通过 if
语句对 num2
和 num3
进行判空处理,如果有值,才会相加。
function sum(num1: number, num2?: number, num3?: number): number
{
console.log(num2); //输出undefined
console.log(num3); //输出undefined
}
sum(1);
注意,可选参数必须在必选参数之后定义,否则会引起编译错误,示例代码如下。
//编译错误:必选参数不能位于可选参数后。ts(1016)
function sum(num1?: number, num2: number): number {
return num1 + num2;
}
默认参数
前一节已经提到,若未向可选参数传递值,则参数值默认为 undefined
。此时建议做判空处理,但这样稍显复杂。在 TypeScript 中,你可以将参数定义为默认参数,为参数设置默认值。之后调用函数时,如果向此参数传入值,则使用传入的值;如果未传值,此参数则会使用预先定义的默认值。
默认参数是通过在参数定义后加上 = 默认值
设置的,示例代码如下。
function sum(num1: number, num2: number = 2, num3: number = 3): number
{
return num1 + num2 + num3;
}
let num4 = sum(1); //1+2(默认值)+3(默认值)等于6
let num5 = sum(1, 4); //1+4+3(默认值)等于8
let num6 = sum(1, 4, 5); //1+4+5等于10
注意,不能将一个参数同时定义为默认参数和可选参数,否则将引起编译错误,示例代码如下。
//编译错误:参数不能包含问号和初始化表达式。ts(1015)
function sum(num1, num2? = 2, num3? = 3): number
{
return num1 + num2 + num3;
}
如果一个参数为默认参数,即使定义参数时没有指明类型,TypeScript 也会将其推导为默认值的类型。
//num1为any类型,因为num2和num3有数值默认值,所以被推导为数值类型
function sum(num1, num2 = 2, num3 = 3): number
{
return num1 + num2 + num3;
}
//由于第一个参数为any类型,以下代码不会引起编译错误
sum("a",2,3);
//以下代码会引起编译错误,因为num2和num3被推导为数值类型
//编译错误:类型"string"的参数不能赋给类型"number"的参数。ts(2345)
sum(1,"b","c");
剩余参数
如果传给函数的参数个数不确定,甚至没有上限,用可选参数或默认参数就不合适。在 TypeScript 中,我们可以定义剩余参数,以便接收不限个数的参数。剩余参数必须定义在函数参数列表的末尾,可以以数组或元组的形式定义,调用时函数一个个依次传入,然后以数组或元组的形式在函数体中使用。
-
数组型剩余参数
当定义数组型剩余参数时,参数的定义方式为 “…参数名称:参数类型[]”。在调用时,传入任意个数的剩余参数,然后以数组的形式在函数体代码中使用,示例代码如下。
function print(memo: string, ...numbers: number[]): void { let printNumberList = ""; if (numbers.length == 0) console.log(`${memo}: 未传入剩余参数`); else { for (let i = 0; i < numbers.length; i++) { printNumberList += numbers[i] + ";" } console.log(`${memo}: ${printNumberList}`); } } print("传入的参数有"); //输出"传入的参数有: 未传入剩余参数" print("传入的参数有", 1, 2); //输出 "传入的参数有: 1;2;" print("传入的参数有", 1, 2, 3, 4, 5); //输出 "传入的参数有: 1;2;3;4;5;"
如果用数组实现前面的求和代码,就可以传入任意个数的参数,代码如下。
function sum(...numbers:number[]): number { let result = 0; for(let i=0;i<numbers.length;i++) { result+=numbers[i]; } return result; } let num1 = sum(1); //值为1 let num2 = sum(1,2); //值为3 let num3 = sum(1,2,3); //值为6
-
元组型剩余参数
当定义元组型剩余参数时,参数定义方式为 “…参数名称:[类型1,类型2,…,类型n]”。在调用时,你必须传入满足元组定义个数和类型的参数,然后以元组的形式在函数体代码中使用,示例代码如下。
function printTuple(...numbers: [number, string]): void {
let printNumberList = "";
for (let i = 0; i < numbers.length; i++) {
printNumberList += numbers[i] + ";"
}
console.log(`传入的参数有${printNumberList}`);
}
printTuple(1,"a"); //输出"传入的参数有1;a;"
注意,如果传入的参数不满足元组定义个数和类型,会引起编译错误,示例代码如下。
function printTuple(...numbers: [number, string]): void {
let printNumberList = "";
for (let i = 0; i < numbers.length; i++) {
printNumberList += numbers[i] + ";"
}
console.log(`传入的参数有${printNumberList}`);
}
//编译错误:没有需要 1 参数的重载,但存在需要 0 或 2 参数的重载。ts(2575)
printTuple("a");
//编译错误:类型"string"的参数不能赋给类型"number"的参数。ts(2345)
printTuple("a", 1);
//编译错误:应有0-2个参数,但获得3个。ts(2554)
printTuple(1, "a", 2);
元组本身也支持可选元素与剩余元素,因此元组型剩余参数还可以用以下方式定义,代码如下。
function printTuple(...numbers: [number, boolean?, ...string[]]): void
{
let printNumberList = "";
for (let i = 0; i < numbers.length; i++) {
printNumberList += numbers[i] + ";"
}
console.log(`传入的参数有${printNumberList}`);
}
printTuple(1); //输出"传入的参数有1;"
printTuple(1, true); //输出"传入的参数有1;true;"
printTuple(1, true, "a", "b", "c"); //输出"传入的参数有1;true;a;b;c;"
返回值
一个函数可以具有返回值,并可以在声明函数时定义返回值类型,返回值使用 return
语句返回。例如,在以下代码中,函数声明的返回值类型为 number
,返回值为数组的长度。
function getArrayLength(array1: string[] = []): number {
return array1.length;
}
let arrayLength = getArrayLength(["a", "b"]); //值为2
当程序执行到 return
语句时,会结束函数的执行,这意味着 return
语句之后的代码不会执行。示例代码如下。
function sum(num1: number, num2: number): number {
return num1 + num2;
console.log("Hello World"); //这句代码永远不会被执行
}
在一个函数中可以有多条 return
语句。例如,以下代码根据分数返回不同的评级字符串。
function getExamResult(score: number): string {
if (score > 90)
return "优秀";
else if (score > 75)
return "良好";
else if (score > 60)
return "及格";
else
return "不及格";
}
let res1 = getExamResult(99); //值为"优秀"
let res2 = getExamResult(55); //值为"不及格"
如果声明时的返回值类型和 return
语句的实际返回值类型不符,会引起编译错误,示例代码如下。
function test(): number {
//编译错误:不能将类型"string"分配给类型"number"。ts(2322)
return "a";
}
一个函数可以没有返回值,当没有返回值时,在函数声明中可将返回值声明为 void
类型,表示该函数没有返回值,示例代码如下。
function sayHello(): void {
console.log("Hello World")!
}
部分情况下,有的函数没有返回值,返回值声明为 void
类型,但它有一些分支可能提前结束函数的执行。此时,你依然可以使用 return
语句,只是 return
语句后不跟返回值,示例代码如下。
function sayHello(name: string = ""): void {
//如果name为空,则跳出函数
if (name=="") {
return;
}
//如果name为空,则以下代码不会执行
console.log("Hello World")!
}
推导返回值类型
TypeScript 能够根据函数体代码中的 return
语句自动推导出返回值类型,因此返回值类型声明可以省略。但基于可读性及编译检查考虑,建议实际编程中,不省略返回值类型。
例如,以下代码声明了一个 test()
函数,它没有显式指定返回值类型,但编译时会自动将其当作返回数值类型的函数。因此调用此函数声明变量时,如果变量无法接收数值类型,就会引起编译错误。
//以下函数根据return语句的内容,推导出函数的返回值类型为number
//以下函数声明等同于function test(num:number):number {...}
function test(num:number) {
return num;
}
//由于函数的返回值类型为number,因此不能将其值赋给string类型的变量
//编译错误:不能将类型"number"分配给类型"string"。ts(2322)
let a:string = test(1);
当函数有多个不同类型的返回值时,编译时也能自动推导出返回值的类型。例如,以下代码根据 num
参数的值返回不同类型的值。
//以下函数声明等同于function test(num: number): number | true | "a" {...}
function test(num: number) {
if (num == 1)
return "a";
else if (num == 2)
return true;
else
return num;
}