为函数定义类型
在 JavaScript
中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression):
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y
}
// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y
}
一个函数有输入和输出,要在 TypeScript
中对其进行约束,那么就需要把输入和输出都考虑到,下面我们就来看看对这两种函数定义的方式在 TypeScript
中分别都是如何进行约束的。
#1.1 函数声明
其中函数声明的类型定义较简单:
function sum(x: number, y: number): number {
return x + y
}
注意,输入多余的(或者少于要求的)参数,是不被允许的:
function sum(x: number, y: number): number {
return x + y
}
sum(1, 2, 3)
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
function sum(x: number, y: number): number {
return x + y
}
sum(1)
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
函数表达式
如果要我们现在写一个对函数表达式(Function Expression
)的定义,可能会写成这样:
let mySum = function (x: number, y: number): number {
return x + y
}
这是可以通过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum
,是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 mySum
添加类型,则应该是这样:
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y
}
注意不要混淆了 TypeScript
中的 =>
和 ES6
中的 =>
。
在 TypeScript
的类型定义中,=>
用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
在 ES6
中,=>
叫做箭头函数,应用十分广泛。
用接口定义函数的类型
我们也可以使用接口的方式来定义一个函数需要符合的形状:
interface SearchFunc {
(source: string, subString: string): boolean
}
let mySearch: SearchFunc
mySearch = function (source: string, subString: string) {
return source.search(subString) !== -1
}
采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
可选参数和默认参数
在 TypeScript
里,每个函数参数都是必须的。 这不是指不能传递 null
或 undefined
作为参数,而是说编译器检查用户是否为每个参数都传入了值。编译器还会假设只有这些参数会被传递进函数。 简单的说,就是传递给一个函数的参数个数必须与函数期望的参数个数一致。
但是在 JavaScript
里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是 undefined
。 在 TypeScript
里我们可以在参数名旁使用 ?
实现可选参数的功能。 比如,我们想让 lastName
是可选的:
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return firstName + '-' + lastName
} else {
return firstName
}
}
需要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了:
function buildName(firstName?: string, lastName: string) {
if (firstName) {
return firstName + ' ' + lastName
} else {
return lastName
}
}
let tomcat = buildName('Tom', 'Cat')
let tom = buildName(undefined, 'Tom')
// index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter.
另外,在 ES6
中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数。当用户没有传递这个参数或传递的参数值是 undefined
时,此时使用我们提供的默认值,我们把它们叫做有默认初始化值的参数。 让我们修改上例,把firstName
的默认值设置为 "A"
。
function buildName(firstName: string = 'A', lastName?: string): string {
if (lastName) {
return firstName + '-' + lastName
} else {
return firstName
}
}
剩余参数
必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在 JavaScript
里,你可以使用 arguments
来访问所有传入的参数。那么在 TypeScript
里,你可以把所有参数都收集到一个变量里。剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意多个。 编译器创建参数数组,名字是你在省略号( ...
)后面给定的名字,你可以在函数体内使用这个数组。
function info(x: string, ...args: string[]) {
console.log(x, args)
}
info('abc', 'c', 'b', 'a')
函数重载
所谓函数重载,即函数名相同, 而形参不同的多个同名函数。在 JavaScript
里, 由于弱类型的特点和形参与实参可以不匹配, 是没有函数重载这一说的。但在 TypeScript
里, 由于有了类型系统,所以就有了函数重载。
/*
* 函数重载: 函数名相同, 而形参不同的多个函数
* 需求: 我们有一个add函数,它可以接收2个string类型的参数进行拼接,也
* 可以接收2个number类型的参数进行相加
*/
// 重载函数声明
function add(x: string, y: string): string
function add(x: number, y: number): number
// 定义函数实现
function add(x: string | number, y: string | number): string | number {
// 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + y
if (typeof x === 'string' && typeof y === 'string') {
return x + y
} else if (typeof x === 'number' && typeof y === 'number') {
return x + y
}
}
console.log(add(1, 2))
console.log(add('a', 'b'))
// console.log(add(1, 'a')) // error