- references
- en
- 函数声明(Function Declaration)
- 函数表达式(Function Expression)
- 箭头函数(Arrow Function)
- 在 js、ts 中,“Lambda 函数”指的就是“箭头函数”
- 函数重载(Function Overloading)
- 函数的类型签名(Function type signature)
- 函数的调用签名(Function call signature)
- 函数的重载签名(Function overload Signature)
- 函数的实现签名(Function implementation signature)
- 可调用的接口(Callable Interface)
- 你在使用 ts 约束变量时,约束的是:变量的值的类型。
- 你在使用 ts 约束函数时,约束的是:函数的参数的类型 + 函数的返回值的类型。
- 函数类型签名是一种用来约束函数的特殊类型,也称为函数调用签名。
- 在 es6 中
=>
是定义箭头函数使用的语法。而在 ts 的类型定义中,=>
可以用来表示函数的类型签名。(...) => ...
其中左边是输入类型,需要用括号括起来,右边是输出类型。 () => void
是一个函数类型签名,它约束的函数没有参数,并且也没有返回值。
let sayHello: () => void;
sayHello = () => console.log("hello world!")
(x: number, y: number) => number
是一个函数类型签名,它约束的函数有两个数字类型的参数,并且会返回一个数字。
let sum: (x: number, y: number) => number;
sum = (a, b) => a + b;
- 若省略函数参数约束信息,ts 会隐式地将参数推断为 any 类型。
function foo(a, b) {
// ...
}
// function foo(a: any, b: any): void
- 开启 noImplicitAny 配置,函数参数被隐式推断为 any 类型的行为将会报错。
// error
function foo(a, b) {
// Parameter 'a' implicitly has an 'any' type.ts(7006)
// Parameter 'b' implicitly has an 'any' type.ts(7006)
}
- 若省略函数的返回类型信息,ts 会根据一些已知的条件推导出函数的返回值的类型
function foo1(name: string): number {
return name.length
}
// ts 推导出 foo1 的类型:
// function foo1(name: string): number
function foo2(name: string) {
return name.length
}
// ts 推导出 foo2 的类型:
// function foo2(name: string): number
foo1 明确声明了参数及返回值的类型,而 foo2 中只声明了参数类型,并没有显式地写明返回类型,但根据我们函数体 return 语句后边跟的内容 name.length
,ts 能够推断出返回类型是 number。
- ts 在推导函数返回值类型时,会将所有“可能”的 return 结果拼接成一个联合类型,并以此作为函数的返回类型。
- ts 虽然可以自行推导函数的返回值类型,但过于简单粗暴,并不可靠。
- 【建议】如果你在编写函数时能够明确返回类型,请将返回类型写清楚,不要过分依赖 ts 的推导结果。
function foo(bar: string) {
return Math.random() < 0.5 ? bar.length : bar;
}
// function foo(bar: string): string | number
在这个示例中,ts 的推断结果是正确的,它能够识别我们的三目运算符,Math.random()
将返回一个介于 0-1 之间的随机数,ts 成功断出了函数返回值的类型可能是 string 或 number。
function foo(bar: string) {
return Math.random() < 5 ? bar.length : bar;
}
// function foo(bar: string): string | number
将 0.5
改为 5
,显然条件 Math.random() < 5
永远成立,返回的只可能是 bar.length
,但 ts 并没有那么智能,依旧认为返回值可能是 string 也可能是 number。
function foo() {
return 1
// Dead Code
return 2
if (false) {
return 3
}
}
// function foo(): 1 | 2 | 3
不妨再测试一下 Dead Code,你可以明确看出,第一条 return 后的所有 code 都是无效代码,但 ts 的处理依旧粗暴,将函数体中所有 return 后边的内容,拼接成一个联合类型 1 | 2 | 3
作为 foo 函数的返回类型。
- 【demo】实现一个 sum 函数,接收两个数字类型的参数,返回这两个数字相加的结果
const sum1 = function (x: number, y: number): number {
return x + y;
};
function sum2(x: number, y: number) {
return x + y;
}
interface ISum {
(x: number, y: number): number;
}
const sum3: ISum = function (x, y) {
return x + y;
};
function sum4(x: number, y: number): number {
return x + y;
}
const sum5: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
const sum6 = (x: number, y: number): number => x + y
const sum7 = (x: number, y: number) => x + y
type TSum = (x: number, y: number) => number
const sum8: TSum = (x, y) => x + y
// ……
约束函数的写法是非常灵活的,上述每一个 sum 函数的声明,描述的类型约束信息都是一致的 (x: number, y: number) => number
,都能够满足该 demo 的要求。其中,高亮的写法是较为推荐的。
如果你追求极致的简洁,那么箭头函数无疑是一个不错的选择。如果你需要将约束信息从函数中抽离出来,那么你可以尝试结合接口 interface
或类型别名 type
来实现。
- 若使用接口来约束函数,那么这个接口也被称为可调用接口(Callable Interface)
interface ISum {
(x: number, y: number): number;
}
- 调用函数时,输入多余的(或者少于要求的)参数,是不被允许的。
function sum1(x: number, y: number): number {
return x + y;
}
sum1(1, 2, 3);
// error 应有 2 个参数,但获得 3 个。
function sum2(x: number, y: number): number {
return x + y;
}
sum2(1);
// error 应有 2 个参数,但获得 1 个。
- ts 中支持可选参数,语法
?:
- 可选参数表示调用函数时,可以选择传递这个参数,也可以选择不传递这个参数
- 可选参数必须位于必选参数之后
let sum = (x: number, y?: number) => {
if (y) {
return x + y;
} else {
return x;
}
}
sum(1) // ok
sum(1, 2) // ok
- 和 js 相同,在 ts 中,也支持参数默认值,语法也是一样的
<参数> = <默认值>
- ts 会将默认参数视作可选的,但是默认参数并不要求必须位于参数列表的结尾。
// ok
let sum1 = (x: number, y?: number) => {
if (y) {
return x + y;
} else {
return x;
}
}
// error 必选参数不能位于可选参数后。
let sum2 = (x?: number, y: number): number => {
// ...
}
// ok
let sum3 = (x: number, y: number = 0) => x + y
// ok
let sum4 = (x: number = 0, y: number) => x + y
- 剩余参数本质上是一个数组,可使用数组类型进行标注
- 【demo】使用剩余参数实现不定参数求和
const sum = (...args: number[]) => args.reduce((pre, cur) => pre + cur, 0)
sum(1) // 1
sum(1, 2) // 3
sum(1, 2, 3) // 6
- 剩余参数也可以使用元组类型进行标注。但这么做没有意义,元组对剩余参数的个数进行了强制约束,相当于间接地改变了剩余参数的行为,让剩余参数失去了意义。
function printPersonInfo(name: string, ...rest: [arg: number, isMale: boolean]) {
console.log('name:', name)
console.log('age:', rest[0])
console.log('isMale:', rest[1])
}
printPersonInfo("abc", 123, true)
// name: abc
// age: 123
// isMale: true
// error 应有 3 个参数,但获得 1 个。
printPersonInfo("abc")
- 如果要表达一个函数没有返回值,我们可以使用
void
类型来约束函数的返回值。
function sayHello(): void {
console.log("hello")
}
sayHello() // hello
- 在 ts 中,函数重载是一种能在同一个函数名称下为不同的参数类型和返回类型提供多个函数类型定义的方式。
- 函数重载使得你可以在相同的函数名称上定义多个函数签名。
- 在某些逻辑较复杂的情况下,函数可能有多组入参类型和返回值类型,这时候就需要用到函数重载来约束函数的调用。
- 与一些其他语言的函数重载不同,ts 的函数重载使用一个统一的函数体处理所有重载,而该函数体必须能够兼容所有的函数重载签名。
- 函数重载是方法名字相同,而参数不同,返回类型可以相同也可以不同
- 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表
function print(s1: string): void;
function print(n1: number, s1: string): void;
function print(x: any, y?: string): void {
console.log(x);
console.log(y);
}
print("abc")
print(1, "xyz");
// abc
// undefined
// 1
// xyz
function combine(input1: number, input2: number): number
function combine(input1: string, input2: string): string
function combine(input1: any, input2: any): any {
if (typeof input1 === "number" && typeof input2 === "number") {
return input1 + input2
} else if (typeof input1 === "string" && typeof input2 === "string") {
return input1.concat(input2)
}
}
const result1 = combine(1, 2)
const result2 = combine("Hello", "TS")
console.log(result1) // 3
console.log(result2) // HelloTS
- 函数重载
- 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理
- 函数重载可以约束函数的调用方式
// 约束函数被调用的方式 1
function combine(input1: number, input2: number): number;
// 约束函数被调用的方式 2
function combine(input1: string, input2: string): string;
function combine(input1: any, input2: any) {
if (typeof input1 === "number" && typeof input2 === "number") {
return input1 + input2;
} else if (typeof input1 === "string" && typeof input2 === "string") {
return input1.concat(input2);
}
}
// ok
combine(1, 2) // 3
// ok
combine('1', '2') // 12
// error 没有与此调用匹配的重载。
combine(1, '2')