函数的注解
函数注解
函数类型包含两部分:参数类型和返回值类型。 当写出完整函数类型的时候,这两部分都是需要的:
// 函数注解是不变的,变的是函数定义的写法
// 完整版函数定义
let myAdd: (x: number, y: number) => number =function(x: number, y: number): number { return x + y; };
// 普通函数定义
let myAdd1: (x: number, y: number) => number = function(x, y){ return x + y;
};
// 箭头函数定义
let myAdd2: (x: number, y: number) => number = (x, y) => x + y;
只要参数类型是匹配的,那么就认为它是有效的函数类型,而不在乎参数名是否正确。
let myAdd: (baseValue: number, increment: number) => number =function(x: number, y: number): number { return x + y; };
推断类型
**ts会根据值进行类型推断:**如果你在赋值语句的一边指定了类型但是另一边没有类型的话,TypeScript编译器会自动识别出类型。
// 函数声明
function add(x:number, y:number): number {return x + y;
}
// 函数表达式
var add1 = function(x:number, y:number): number{return x + y;
}
// es6函数表达式
var add1 = (x:number, y:number): number => x + y;
函数作为对象使用接口注解
- 函数也是对象,还可以通过接口注解函数。
interface AddInterface{(x:number, y:number): number; //这里返回值前面是冒号
}var add1: AddInterface = (x, y) => x + y;
// 接口就是一个对象
var add2: {(x:number, y:number): number} = (x, y) => x + y;
// 函数注解
var add:(x:number, y:number) => number = (x, y) => x + y;
- 本身对象,通过接口注解对象
前两个写法是等效的, 第三种写法接口中不允许
interface AddInterface{// a是一个函数a(x:number, y:number): number//a是一个对象的一个属性,属性值为一个函数,函数的返回值是number类型a: (x:number , y:number) => number// a: function(x:number, y:number):number
}var add:AddInterface = {a: function(x:number, y:number): number{return x+y;}
}
- 引申:使用接口注解构造函数
当接口中函数前面有new
,作为构造函数的注解(这里的构造函数使用class方式模拟)
interface AddInterface{new (x:number, y:number): void
}class Test{constructor(x:number, y:number){}
}
var test: AddInterface = Test;
函数可选参数,默认参数,剩余参数
TypeScript里的每个函数参数都是必须的。简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致。
可选参数
在TypeScript里我们可以在参数名旁使用 ?
实现可选参数的功能。
可选参数必须跟在必须参数后面。 想让last name是可选的
function buildName(firstName: string, lastName?: string) {if (lastName)return firstName + " " + lastName;elsereturn firstName;
}let result1 = buildName("Bob"); // works correctly now
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
默认参数
在TypeScript里,用户没有传递这个参数或传递的值是undefined
时。 它们叫做有默认初始化值的参数。
在所有必须参数后面的带默认初始化的参数都是可选的
function buildName(firstName: string, lastName = "Smith") {return firstName + " " + lastName;
}let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result4 = buildName("Bob", "Adams"); // ah, just right
与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。如果带默认值的参数出现在必须参数前面,用户必须明确的传入 undefined
值来获得默认值。
function buildName(firstName = "Will", lastName: string) {return firstName + " " + lastName;
}let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"
剩余参数
必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用 arguments
来访问所有传入的参数。
在TypeScript里,你可以把所有参数收集到一个变量里:
function buildName(firstName: string, ...restOfName: string[]) {return firstName + " " + restOfName.join(" ");
}let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( ...
)后面给定的名字,你可以在函数体内使用这个数组。
这个省略号也会在带有剩余参数的函数类型定义上使用到:
function buildName(firstName: string, ...restOfName: string[]) {return firstName + " " + restOfName.join(" ");
}let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
this和箭头函数
this和箭头函数
箭头函数能保存函数创建时的 this
值,而不是调用时的值。
看一个案例
let deck = {suits: ["hearts", "spades", "clubs", "diamonds"],cards: Array(52),createCardPicker: function() {return function(){let pickedCard = Math.floor(Math.random() * 52);let pickedSuit = Math.floor(pickedCard / 13);// 指出问题:"this" 隐式具有类型 "any",因为它没有类型注释。return {suit: this.suits[pickedSuit], card: pickedCard % 13};}}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
第二个语句执行报错,执行时内部的this指向window(严格模式下指向undefined)。
可以更改配置项,来不提示错误,但是this指向仍未确定。
“noImplicitThis”: false
想要在一开始就明确this的指向,指向对象本身,方法:
- 对象的函数表达式内使用箭头函数嵌套
- 使用一个显式的this参数。
方法一:(常用)
let deck = {suits: ["hearts", "spades", "clubs", "diamonds"],cards: Array(52),createCardPicker: function() {// NOTE: the line below is now an arrow function, allowing us to capture 'this' right herereturn () => {let pickedCard = Math.floor(Math.random() * 52);let pickedSuit = Math.floor(pickedCard / 13);return {suit: this.suits[pickedSuit], card: pickedCard % 13};}}
}
方法二:
interface Card {suit: string;card: number;
}interface Deck {suits: string[];cards: number[];// 函数的返回值是一个箭头函数,箭头函数的返回类型是Card类型createCardPicker(this: Deck): () => Card;
}let deck: Deck = {suits: ["hearts", "spades", "clubs", "diamonds"],cards: Array(52),// NOTE: The function now explicitly specifies that its callee must be of type DeckcreateCardPicker: function(this:Deck) {return () =>{let pickedCard = Math.floor(Math.random() * 52);let pickedSuit = Math.floor(pickedCard / 13);return {suit: this.suits[pickedSuit], card: pickedCard % 13};}}
}
this在回调函数里作为参数
接口中的this是void类型,即不指定this为任何类型。
// this在回调函数参数里
interface UIElement {addClickListener(onclick: (this: void, e: Event) => void): void;
}
//下面是伪代码
class Handler {info: string;onClickBad(this: Handler, e: Event) {// oops, used this here. using this callback would crash at runtimethis.info = e.message;}
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!
指定接口中回调函数参数this为void类型后,传入的函数参数也需要是void类型否则报错。
修改方法:
- 类原型方法中,不再使用this。
- 类原型方法中想要使用this,且指定this为void类型,使用箭头函数
class Handler {info: string;onClickBad(this: void, e: Event) {// 不使用this}
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad);
这是可行的因为箭头函数不会捕获this
,所以你总是可以把它们传给期望this: void
的函数。
class Handler {info: string;onClickGood = (e: Event) => { this.info = e.message }
}
函数重载
ts中一个函数中根据传入函数参数类型,执行不同的函数体。
这里的函数返回值类型为void,而**当函数返回值类型设为void,则函数执行的返回值仍能保持原样,保持兼容。**
function reverse(target: number | string) {if(typeof target === 'number'){console.log([target.toString()]) //[ '1236789' ]console.log([...target.toString()]) //['1', '2', '3','6', '7', '8','9']return [...target.toString()].reverse().join();}if(typeof target === 'string'){return target.split('').reverse().join('')}
}
console.log(reverse('js++')); //'++sj'
console.log(reverse(1236789)); //9,8,7,6,3,2,1
函数定义(重载的签名)相当于:
function reverse(target:number):void;
function reverse(target:string):void;
这样签名实现了返回值兼容,即返回的值的类型与void不符仍能通过检查。
实现时,至少传入一个参数。
类型别名type
:给一个类型起个新名字。一般用于联合类型。
type str = string | number;
type voidFunc = () => void;
var fn1: voidFunc = () => true;
console.log(fn1()); // true
当函数返回值类型设为void,则函数执行的返回值仍能保持原样,保持兼容。