我们之前已经接触过了,给函数中的参数和返回值规定类型,
function add(x: number, y: number): number {return x + y;}let myAdd = function(x: number, y: number): number { return x + y; };
但这只是”给函数本身添加类型“罢了,并不是说定义了一个函数”类型“。在TS中我们除了用function或者匿名函数外,也有规定的函数定义方式。
完整的函数定义
现在我们已经为函数指定了类型,下面让我们写出函数的完整类型。
let myAdd: (x: number, y: number) => number =function(x: number, y: number): number { return x + y; };
你应该注意到了,在赋值之前有一段很像箭头函数的表达式:(x: number, y: number) => number ,没错这就是TS中表示函数的定义式。
函数类型包含两部分:参数的类型的和返回值的类型。 当写出完整函数类型的时候,这两部分都是需要的。 我们以参数列表的形式写出参数类型,为每个参数指定一个名字和类型。 这个名字只是为了增加可读性。
对于返回值类型,我们在函数和返回值类型之前使用( =>)符号,就像箭头函数那样,使之清晰明了。 如之前提到的,返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为 void而不能留空。
推断类型
如果你在赋值语句的一边指定了类型但是另一边没有类型的话,TypeScript编译器会自动识别出类型:
// 右边的参数 `x` 和 `y` 具有类型numberlet myAdd: (baseValue: number, increment: number) => number =function(x, y) { return x + y; };
这叫做“按上下文归类”,是类型推论的一种。 它帮助我们更好地为程序指定类型。
函数重载
JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。这就是我们熟悉的重载,但是,和Java中不太一样的是,你不需要重复写多个具体的实现。TS重载的方法是为同一个函数提供多个函数类型定义来进行函数重载。 编译器会根据这个列表去处理函数的调用。
let suits = ["hearts", "spades", "clubs", "diamonds"];function pickCard(x: {suit: string; card: number; }[]): number; //重载function pickCard(x: number): {suit: string; card: number; }; //重载function pickCard(x): any { // 不是重载,具体实现// Check to see if we're working with an object/array// if so, they gave us the deck and we'll pick the cardif (typeof x == "object") {let pickedCard = Math.floor(Math.random() * x.length);return pickedCard;}// Otherwise just let them pick the cardelse if (typeof x == "number") {let pickedSuit = Math.floor(x / 13);return { suit: suits[pickedSuit], card: x % 13 };}}let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];let pickedCard1 = myDeck[pickCard(myDeck)];alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);let pickedCard2 = pickCard(15);alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
这样改变后,重载的pickCard函数在调用的时候会进行正确的类型检查。
为了让编译器能够选择正确的检查类型,它与JavaScript里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。
注意,function pickCard(x): any并不是重载列表的一部分,因此这里只有两个重载:一个是接收对象另一个接收数字。 以其它参数调用 pickCard会产生错误。
剩余参数
在JavaScript里,你可以在函数中使用 arguments对象 ,这个对象是内置的可以直接使用,来访问所有传入的参数。
function show() {console.log(arguments); // arguments会收集所有传入函数的参数}show(1, 2, 3, 4, 5) // 输出:[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }

可以看到,arguments对象是一个伪数组。(话说java中的args[]也就是获得所有剩余参数的意思,哦 那路或多)
以上是JS中的方法。
不推荐在箭头函数中使用arguments和this !!,因为他们都会找到上一级作用域中。
在TypeScript里,你可以把所有参数收集到一个变量里,我们之前在解构和展开时也提到了:
function buildName(firstName: string, ...restOfName: string[]) {return firstName + " " + restOfName.join(" ");}let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( …)后面给定的名字,你可以在函数体内使用这个数组。
是不是有点眼熟?在python中也是这样定义剩余参数的,…args 代表任意个可选参数。现在在TS中也能这样做了,哇哦。
这个省略号也能在带有剩余参数的函数类型定义上使用:
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
并且,TS中默认不允许在箭头函数中使用arguments,会直接报错。
可选参数和默认参数
可选参数
对于可选参数,我们之前已经多少了解过,这里只重点提一个需要注意的细节:
可选参数必须在必选参数后面。 因为函数默认会先接受必选参数,之后才是可选参数和剩余参数。
function buildName(firstName: string, lastName?: string) {if (lastName)return firstName + " " + lastName;elsereturn firstName;}let result1 = buildName("Bob"); // 正确let result2 = buildName("Bob", "Adams", "Sr."); // 错误,额外参数let result3 = buildName("Bob", "Adams"); // 正确
如果上例我们想让firstName是可选的,那么就必须调整它们的位置,把firstName放在后面。
默认参数
之前在对象解构那一章也了解过了,你可以给函数的参数预设一个默认值,当这个参数没有传入或者传入的值为 undefined 的时候,就会使用这个默认值。
function buildName(firstName: string, lastName = "Smith") {return firstName + " " + lastName;}let result1 = buildName("Bob"); // 正常调用let result2 = buildName("Bob", undefined); // 与第一个是等价的let result3 = buildName("Bob", "Adams", "Sr."); // 错误,多了参数let result4 = buildName("Bob", "Adams"); // 正确,不使用默认值
这里也只再说一个重要的细节:
与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。 但如果带默认值的参数出现在必须参数前面,用户必须明确的传入 undefined 值来使用默认值(null不行)。 例如,我们重写上面的例子,让 firstName是带默认值的参数:
function buildName(firstName = "Will", lastName: string) {return firstName + " " + lastName;}let result1 = buildName("Bob"); // 错误,缺少一个lastName 的参数let result2 = buildName("Bob", "Adams", "Sr."); // 错误。额外参数let result3 = buildName("Bob", "Adams"); // okaylet result4 = buildName(undefined, "Adams"); // 正确,使用默认值
