函数类型
为函数定义类型
在 TypeScript 中,我们可以为函数定义明确的类型,包括参数的类型和返回值的类型。这样有助于提供更好的类型检查,并在编码过程中捕获潜在的错误。
typescript
function add(arg1: number, arg2: number): number {
return x + y;
}
在例子中,我们定义了一个名为 add 的函数,它接受两个参数 arg1 和 arg2,都被明确地标注为 number 类型。同时,我们也为函数指定了返回值的类型,即 number。
这样的类型定义有几个优势:
- 类型检查: TypeScript 编译器会检查函数调用时传递的参数类型是否与定义一致,以及函数返回值是否符合声明的类型。
- 代码提示: 编辑器(如 VSCode)会根据定义的类型提供更精准的代码提示,提高开发效率。
- 错误捕获: 如果函数内部逻辑与声明的类型不符,编译器会报错,帮助我们尽早发现并修复问题。
如果省略参数的类型,TypeScript 会尝试推断参数的类型,但最好是显式声明以确保类型的清晰性。如果省略返回值的类型,TypeScript 会尝试根据函数内部的逻辑推断返回值的类型,但同样最好是显式声明以提高代码可读性。
完整的函数类型
typescript
let add: (x: number, y: number) => number;
add = (arg1: number, arg2: number): number => arg1 + arg2;
add = (arg1: string, arg2: string): string => arg1 + arg2; // error
//不能将类型"(arg1: string, arg2: string) => string"分配给类型"(x: number, y: number) => number"。
例子演示了如何定义一个函数类型,然后将该类型用于变量 add。
定义函数类型:
typescript
let add: (x: number, y: number) => number;
定义了一个变量 add,其类型是一个函数,该函数接受两个参数 x
和 y
,均为 number 类型,返回值为 number 类型。
赋予实际函数:
ini
add = (arg1: number, arg2: number): number => arg1 + arg2;
将一个实际的函数赋值给了 add 变量。这个实际函数的参数类型和返回值类型与我们在函数类型中定义的一致,所以这个赋值操作是合法的。
赋予不符合类型的函数
csharp
add = (arg1: string, arg2: string): string => arg1 + arg2; // error
试图将一个参数和返回值类型均为 string 的函数赋值给 add,但这会导致类型不匹配的错误。因为此时 add 的类型期望参数和返回值都是 number 类型,而不是 string。
使用接口定义函数类型
typescript
interface Add {
(x: number, y: number): number;
}
let add: Add = (arg1: string, arg2: string): string => arg1 + arg2; // error 不能将类型"(arg1: string, arg2: string) => string"分配给类型"Add"
例子使用了接口 Add 来定义一个函数类型,然后将这个接口用于变量 add。
1.定义函数类型接口:
typescript
interface Add {
(x: number, y: number): number;
}
这个接口 Add 描述了一个函数类型,该函数接受两个参数 x 和 y,均为 number 类型,返回值为 number 类型
2.使用接口定义函数变量:
csharp
let add: Add = (arg1: string, arg2: string): string => arg1 + arg2; // error
尝试将一个参数和返回值类型均为 string 的函数赋值给 add 变量,但这违反了接口 Add 中定义的函数类型。因为接口要求参数和返回值的类型应该是 number,而不是 string。
TypeScript 在这里会报错,提示我们无法将给定的函数类型赋值给 Add 接口。
使用类型别名定义函数类型
可以使用类型别名来定义函数类型
typescript
type Add = (x: number, y: number) => number;
let add: Add = (arg1: string, arg2: string): string => arg1 + arg2; // error 不能将类型"(arg1: string, arg2: string) => string"分配给类型"Add"
使用 type
关键字创建了一个类型别名 Add,它表示一个接受两个参数(都是 number 类型)并返回一个 number 类型的函数。具体的定义是 (x: number, y: number) => number。
然后,我们声明一个变量 add,并将 Add 类型别名用于类型注解,表示这个变量是一个符合 Add 定义的函数类型的变量。
但在赋值时,我们尝试将一个返回 string 类型的函数赋给 add,这导致了 TypeScript 报错:
// error 不能将类型"(arg1: string, arg2: string) => string"分配给类型"Add"
因为我们在类型别名 Add 中规定了函数的返回类型应该是 number,但实际上我们尝试将返回类型为 string 的函数赋给它,所以 TypeScript 给出了类型不匹配的错误。
参数
可选参数
在 TypeScript 中,你可以通过在函数参数后面加上 ? 符号来定义可选参数。可选参数表示在调用函数时,这个参数可以传递也可以不传递,不传递时会使用默认值或者是 undefined。
需要注意的是,可选参数必须放在参数列表的最后。如果一个参数是可选的,那么它后面的所有参数也必须是可选的。
typescript
type Add = (x?: number, y: number) => number; // error 必选参数不能位于可选参数后。
看以下例子:
typescript
type Add = (x: number, y: number) => number;
let add: Add = (arg1: string, arg2: string): string => arg1 + arg2;
add(1, 2); // right
add(1, 2, 3); // error 应有 2 个参数,但获得 3 个
add(1); // error 应有 2 个参数,但获得 1 个
当使用类型别名或接口定义函数类型时,函数的参数个数和类型是严格检查的。因此,如果你声明了一个类型 Add,表示一个接受两个参数类型为 number 的函数并返回 number,那么在使用这个类型定义的函数时,必须传递且只能传递两个参数,且这两个参数的类型必须为 number。
默认参数
在 TypeScript 中,你可以为函数参数设置默认值,这样在调用函数时如果没有提供相应参数,就会使用默认值。默认参数的设置通过在参数后面使用等号 = 进行
typescript
// TypeScript
function greet(name: string, greeting: string = "Hello"): void {
console.log(`${greeting}, ${name}!`);
}
// Valid function calls
greet("John"); // Output: Hello, John!
greet("Jane", "Good morning"); // Output: Good morning, Jane!
// No strict type checking, so this is also valid
greet("Bob", 42); // Output: 42, Bob! (TypeScript may issue a warning)
greeting
参数有一个默认值 "Hello"。当调用 greet 函数时,如果没有提供 greeting 参数,它将使用默认值 "Hello"。如果提供了 greeting 参数,它将使用传递的值。
需要注意的是,当使用默认参数时,TypeScript 会根据默认值自动推断参数的类型。在上例中,greeting 的类型被推断为 string。如果你传递了不同类型的值(例如数字),TypeScript 可能会发出警告,但仍然允许这样的调用。
剩余参数
在 TypeScript 中,你可以为剩余参数(Rest Parameters)指定类型。剩余参数是一个以省略号 ... 开头的标识符,它表示函数接受任意数量的参数,并将它们放入一个数组中。
typescript
const handleData = (arg1: number, ...args: number[]) => {
//
};
handleData(1, "a"); // error 类型"string"的参数不能赋给类型"number"的参数
handleData函数接受一个 arg1 参数和任意数量的其他参数,这些其他参数会被收集到 args 数组中。你可以在函数体内使用这个数组进行进一步的处理。
需要注意的是,由于已经为 arg1
指定了类型为 number,如果调用函数时传递了类型不符合的参数,TypeScript 会进行类型检查并报错
函数重载
在 TypeScript 中,函数重载是一种为同一个函数提供多个不同的函数签名(类型定义)的机制。这些不同的函数签名描述了函数的不同调用方式,包括参数类型、个数以及返回值类型。
typescript
function greet(person: string): string; // 函数签名1
function greet(person: string, age: number): string; // 函数签名2
function greet(person: string, age?: number): string {
if (age === undefined) {
return `Hello, ${person}!`;
} else {
return `Hello, ${person}! You are ${age} years old.`;
}
}
// 调用函数
console.log(greet("Alice")); // Hello, Alice!
console.log(greet("Bob", 25)); // Hello, Bob! You are 25 years old.
在这个例子中,我们定义了两个函数签名。
第一个函数签名接受一个 string 类型的参数,并返回一个 string 类型的值。
第二个函数签名接受一个 string 类型的参数和一个可选的 number 类型的参数,同样返回一个 string 类型的值。
实体函数部分是实际的函数实现,根据传入参数的不同,它会在两个函数签名中选择一个执行相应的逻辑。
注意事项和一些要点:
- 函数实体部分的参数和返回值的类型要符合函数签名的定义。
- 函数签名中的参数列表和返回值类型构成了函数的特定调用方式。
- 在调用时,TypeScript 会根据传入的参数类型和个数,选择合适的函数签名进行匹配。
- 函数实体部分通常包含最宽泛的类型,以适应所有可能的函数签名。
- 函数重载可以提高代码的可读性和健壮性,让 TypeScript 在调用函数时能够更精确地推断和检查类型。