blog
blog copied to clipboard
typescript手册-函数的类型
函数的定义方式
函数的定义方式有两种:函数声明和函数表达式
// 函数声明
function add(x, y) {
return x+y;
}
// 函数表达式
let reduce = function(x, y) {
return x - y;
}
函数的类型
一个函数有输入输出,对函数类型的约束本质上是对输入值和输出值的约束,为上面两个函数添加类型声明
函数声明
函数声明的类型定义较简单:
function add(x:number, y:number):number {
return x + y;
}
这里我们定义了参数x
和y
的类型为number
,返回值类型也为number
,除了类型的约束,参数的个数也限制为2
个,输入多余或者少于要求的参数,是不被允许的
function add(x:number, y:number):number {
return x + y;
}
add(1, 2, 3);
// Expected 2 arguments, but got 3
function add(x:number, y:number):number {
return x + y;
}
add(1);
// Expected 2 arguments, but got 1
函数表达式
我们对上面的函数表达式定义类型:
let reduce = function(x:number, y:number):number {
return x - y;
}
上面我们仅仅对等号右侧的匿名函数进行类型定义,等号左边的reduce
是通过赋值之后类型推导推断出来的,准确的来说叫上下文归类,是类型推导的一种,事实上我们也可以给reduce
定义类型:
下面是完整的类型声明:
let reduce: (x:number, y:number) => number = function(x:number, y:number):number {
return x - y;
}
整个(x:number, y:number) => number
是对reduce的类型定义,注意 =>
并非是ES6的箭头函数,它表示函数输入输出之间的管道,左侧是函数输入值,右侧是函数输出值
好的习惯
我们多次强调函数是由输入值和输出值组成的,即使函数没有任何返回值,我们也要必须指定返回值类型为void
而不能留空。
用接口定义函数的形状
我们可以用接口描述函数的形状
interface ReduceFunc {
(x:number, y:number):number
}
let reduce:ReduceFunc;
reduce = function(x:number, y:number):number {
return x - y;
}
可选参数
前面我们提到,如果对函数的参数进行约束,输入多余或者少于要求的参数,是不被允许的,那么我们如何定义可选的参数呢?
function getName(firstName: string, lastName?:string) {
if (lastName) {
return firstName + ',' + lastName
} else {
return firstName
}
}
getName("zhangsan");
getName("zhangsan", "lisi");
上面的例子我们通过在参数后加?表示这是一个可选参数,需要注意的是,可选参数后面不能出现必选参数:
function getName(firstName: string, lastName?:string, thirdName) {
if (lastName) {
return firstName + ',' + lastName
} else {
return firstName
}
}
这样编译会直接报错:
A required parameter cannot follow an optional parameter.
参数默认值
同ES6一样,typescript支持给函数参数设置默认值
function getName(firstName: string, lastName:string = 'jack') {
if (lastName) {
return firstName + ',' + lastName
} else {
return firstName
}
}
getName("tom"); // 输出tom,jack
参数默认值是不受可选参数必须在必选参数之后的限制了
function getName(lastName:string = 'jack', firstName: string) {
return firstName + ',' + lastName
}
getName(undefined, "tom");
但是即使lastName
不赋值,也要用undefined
占位,否则typescript
不知道tom
是赋值给lastName
还是firstName
,编译将会报错
function getName(lastName:string = 'jack', firstName: string) {
return firstName + ',' + lastName
}
getName("tom");
function getName(lastName:string = 'jack', firstName: string) {
~~~~~~~~~~~~~~~~~
An argument for 'firstName' was not provided.
剩余参数
在ES6中,使用...rest
收集函数中的剩余参数
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
注意rest参数只能是最后一个参数
因为items是一个数组,所有我们可以用数组的类型来定义它
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
函数重载
javascript
里函数根据传入不同的参数而返回不同类型的数据是很常见的。比如我们实现函数reverse,当输入数字123
,返回值是反转的321
,输入字符串hello
,返回值是反转的olleh
function reverse(x:number|string):number|string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse().join('')
}
}
上面的函数能满足我们的需求,但是有一个问题是不能精确的表达出:输入的是数字,输出的也是数字,输入的是字符串,输出的也是字符串
这个时候我们使用重载定义多个reverse函数:
function reverse(x:number):number
function reverse(x:string):string
function reverse(x:number|string):number|string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse().join('')
}
}
上面的例子我们需要注意:
- 我们重复定义了
reverse
函数,但前几次是函数定义,只有最后一次是函数实现 - 编译器会从第一个定义开始查找符合的类型,因此越精确的定义应该放在越前面
- 最后一次定义
function reverse(x:number|string):number|string
不会被作为函数重载,因此这里的重载只有两个:一个接受number
一个接受string
比如我们再最后一次定义的时候添加了boolean类型的参数和返回值:
function reverse(x:number):number
function reverse(x:string):string
function reverse(x:number|string|boolean):number|string|boolean {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse().join('')
}
}
reverse(true)
编译将会报错:
Argument of type 'true' is not assignable to parameter of type 'string'
由此可见我们最后一次定义函数是不会计入重载列表的,编译器在找到sring
之后就停止类型检查,抛出异常。
参考: