TypeScript入门三:TypeScript函数类型
- TypeScript函数类型
- TypeScript函数的参数
- TypeScript函数的this与箭头函数
- TypeScript函数重载
一、TypeScript函数类型
在上一篇博客中已经对声明TypeScript类型变量已经做了初步的解析,这里先回顾以下:
1 //声明函数 2 function add1(x:number,y:number):number{ 3 return x+y; 4 } 5 6 let add2 = function(x:number,y:number):number{ 7 return x + y; 8 } 9 10 //定义函数类型 11 type myAdd = (baseValue: number, increment: number) => number; 12 //根据函数类型声明函数变量 13 let myAdd1:myAdd = function(x:number,y:number) : number{ 14 return x + y; 15 } 16 let myAdd2:myAdd = function(x:number,y:number) : number{ 17 return x - y; 18 } 19 20 let hintStamp(str:string):void{ 21 console.log(str); 22 }
关于函数类型有一点需要注意,需要使用type关键字并使用等号"="赋值类型;如果使用let声明并使用冒号冒“:”表示的是声明一个函数变量,并且这个函数变量有指定的类型,这个变量不能作为函数类型,只能给它自己赋值指定类型的函数。
二、TypeScript函数的参数
2.1 根据函数类型声明的函数变量,函数的实际参数名称可以不与函数类型的参数名称一致,这一点与对象类型Object的字段有点区别;
1 type myAdd = (baseValue: number, increment: number) => number; 2 let myAdd1:myAdd = function(a:number,b:number) : number{ 3 return a + b; 4 } 5 let myAdd2:myAdd = function(x:number,y:number) : number{ 6 return x - y; 7 }
2.2 可选参数与默认参数:
这部非内容在官方文档中有详细的说明,这里简要的解析以下,如果有不明白的建议查看官方文档。
可选参数意思就是函数的参数在实际调用函数时,可以不需要全部对应传入,但是在定义函数类型时,必须设定默认参数。
1 //示例:在定义函数类型时,给参数b设定了默认参数:'TypeScript' 2 function foo (a: string,b = 'TypeScript'): void{ 3 console.log(a + ':' + b); 4 } 5 foo('hello');//hello:TypeScript
设置了默认参数,就不需要设置参数的类型了,TypeScript的IDE会主动为我们推断出参数的类型,也就是默认值的类型,如果给可选参数传值就必须对应默认参数的类型。
除了设置既定的默认参数以外,还可以使用(参数名+?)的方式设置非特定参数,但是设置默认非特定参数需要设置默认参数的类型(参数名?:类型)。这种不设定特定值的可选参数,调用函数时不给这个可选参数传值的话就会默认为undefined。
1 //这是官方文档中的一个示例: 2 function buildName(firstName: string, lastName?: string) { 3 if (lastName){ 4 5 return firstName + " " + lastName; 6 }else{ 7 return firstName; 8 } 9 } 10 11 let result1 = buildName("Bob"); // works correctly now 12 console.log(result1); 13 let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters 14 let result3 = buildName("Bob", "Adams"); // ah, just right
2.3 剩余参数
关于剩余参数其本质就是ES6的收集(res)语法,使用"..."将多余的参数收集,但是TypeScript转换到ES5的js代码时还是需要使用arguments来实现,毕竟ES5版本中还没有收集(res)语法。详细可以了解ES6入门一:块级作用域(let&const)、spread展开、rest收集
1 function buildName(firstName: string, ...restOfName: string[]) { 2 return firstName + " " + restOfName.join(" "); 3 } 4 5 let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
但是需要注意,收集语法产生的数据必然是数组,所以必须使用数组类型。
三、TypeScript函数的this与箭头函数
关于this指向在JavaScript中一直是比较容易被它击晕的一个知识点,不要担心,我这里有一篇博客包治百病:JavaScript中的this指向规则
当然ES6的相关语法产生的this指向规则变化这里也有:ES6入门五:箭头函数、函数与ES6新语法
当然好不了严格模式的this指向问题:JavaScript严格模式
TypeScript并不改变JavaScript的this指向规则,而是在IDE中提供了更友好的警告提示,这个功能需要在tsconfig.json中通过"noImplicitThis": true开启。我们知道在JavaScript中this指向规则是比较复杂的,而TypeScript也根据这些规则做出了相应的调整,这里关于TypeScript函数的this指向其实就是了解"noImplicitThis"给我们的提示规则是什么?
3.1 TypeScript对象返回函数中的this会警告提示:
1 let obj = { 2 strs : ["aaa","bbb","ccc","ddd"], 3 fun:function(){ 4 return function() { //这里会提示this出错,因为在严格模式下this可能为undefined,在TypeScript中IDE识别this类型为any,所以被认为可能出现undefined情况 5 let index = Math.floor(Math.random() * this.strs.length); 6 return this.strs[index]; 7 } 8 } 9 } 10 var fun = obj.fun(); 11 console.log(fun());
这个问题可以使用箭头函数来解决:
1 let obj = { 2 strs : ["aaa","bbb","ccc","ddd"], 3 fun:function(){ 4 return () => { //通过箭头函数解决 5 let index = Math.floor(Math.random() * this.strs.length); 6 return this.strs[index]; 7 } 8 } 9 } 10 var fun = obj.fun(); 11 console.log(fun());
3.2 this作为函数参数,不会出现错误提示,这一点需要注意,因为在原生JavaScritp种这种写法会直接出现语法错误。例如示例中这样写:
1 function f(this:void){//这里你可以给形参任意TypeScript类型都不会报错 2 //很遗憾这里并不会报错 3 }
这里有几方面的原因,严格模式下函数只执行内部this都是指向undefined,如果函数被对象调用this必然也就指向调用函数的对象。更关键的是被写在形参内的this根本就不会被TypeScript编译到原生js代码中,这里不报错其实是TypeScript将这个低级的错误帮你自动处理了,所以就静默了这个错误提示。
关于this作为参数在TypeScript中还有另一个功能,就是帮助函数维持this指向,例如官方这个示例:
1 interface Card { 2 suit: string; 3 card: number; 4 } 5 interface Deck { 6 suits: string[]; 7 cards: number[]; 8 createCardPicker(this: Deck): () => Card; 9 } 10 let deck: Deck = { 11 suits: ["hearts", "spades", "clubs", "diamonds"], 12 cards: Array(52), 13 // NOTE: The function now explicitly specifies that its callee must be of type Deck 14 createCardPicker: function(this: Deck) { 15 return () => { 16 let pickedCard = Math.floor(Math.random() * 52); 17 let pickedSuit = Math.floor(pickedCard / 13); 18 19 return {suit: this.suits[pickedSuit], card: pickedCard % 13}; 20 } 21 } 22 } 23 24 let cardPicker = deck.createCardPicker(); 25 let pickedCard = cardPicker(); 26 27 alert("card: " + pickedCard.card + " of " + pickedCard.suit);
上面这段代码中14行的createCardPicker: function(this: Deck){}被编译成了这样:
1 createCardPicker: function () { 2 var _this = this; 3 }
上面这种代码是我们常用维持this指向的手段,在定义接口时常见的代码,在TypeScript中它使用this作为参数的方式来实现,这样的处理方式可以非常明显的看到this指向了对象自身,并且如果这个方法被赋值给其他对象变量时同样会静默这个this参数的错误,而其他对象在TypeScript严格的变量类型语法中也可能获取这个方法作为自身参数。
3.3 this在回调函数中如何维持this指向?下面是将官方文档代码补全的示例:
1 interface UIElement { 2 addClickListener(onclick: (this: void, e: Event) => void): void; 3 } 4 5 class Handler { 6 info: string; 7 constructor(infoStr:string){ 8 this.info = infoStr; 9 } 10 onClickGood = (e: Event) => {//通过箭头函数来保持函数内部this不变 11 // can't use this here because it's of type void! 12 console.log(this);//指向Handler的实例对象 13 console.log(this.info);//info 14 console.log('clicked!'); 15 } 16 } 17 let h = new Handler('info'); 18 class uiElementClass implements UIElement{ 19 addClickListener(onclick: (this: void, e: Event) => void): void{ 20 onclick(new Event('event')); 21 } 22 } 23 let uiElement:uiElementClass = new uiElementClass(); 24 uiElement.addClickListener(h.onClickGood);
四、TypeScript函数重载
关于TypeScript函数我想了好久就是不知道怎么解析这个东西,照着示例套吧,我也只能复制官方代码放这里了。
1 let suits = ["hearts", "spades", "clubs", "diamonds"]; 2 3 function pickCard(x: {suit: string; card: number; }[]): number; 4 function pickCard(x: number): {suit: string; card: number; }; 5 function pickCard(x:any): any { 6 // Check to see if we're working with an object/array 7 // if so, they gave us the deck and we'll pick the card 8 if (typeof x == "object") { 9 let pickedCard = Math.floor(Math.random() * x.length); 10 return pickedCard; 11 } 12 // Otherwise just let them pick the card 13 else if (typeof x == "number") { 14 let pickedSuit = Math.floor(x / 13); 15 return { suit: suits[pickedSuit], card: x % 13 }; 16 } 17 } 18 19 let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }]; 20 let pickedCard1 = myDeck[pickCard(myDeck)]; 21 alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); 22 23 let pickedCard2 = pickCard(15); 24 alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);