函数
和js一样,ts函数可以创建有名字的函数和匿名函数。
在es5中定义一个function有这么两种方式
// 命名函数
function fun(ags) {
return ags;
}
// 匿名函数
let fun = function(ags) {
return ags
};
函数类型
在ts中变量都是有固定的类型的,函数同样需要指定类型,包括参数和返回值
function toStr(x: number): string {
return x + '';
}
let myAdd = function(x: number, y: number): number {
return x + y;
};
//没有返回值的函数
function noRtn(x:string):void{
console.log('没有返回值的函数')
}
ts能够根据返回语句自动推断出返回值类型,换句话说返回值的类型可以省略。
function toStr(x: number){
return x + '';
}
let myAdd = function(x: number, y: number){
return x + y;
};
function noRtn(x:string){
console.log('没有返回值的函数')
}
至于es6的箭头函数这里需要注意一下,在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
//ts函数完整写法
let _Add: (x: number, y: number) => number =
function(x: number, y: number): number { return x + y; };
//箭头函数
let fun = (a:string)=>{console.log(1)}
可选参数
ts里的每个函数参数都是必须的。 编译器检查用户是否为每个参数都传入了值,就是传递给一个函数的参数个数必须与函数定义的参数个数一致。
let _add=(a:number,b:number)=>{return a+b;};
_add(1,2) //fine
_add(1) //error
_add(1,2,3) //error
如果是入参数量不确定则在定义参数后加上‘?’将他变成可选参数,并且可选参数必须跟在必须参数后面;
function _add(a: string, b?: string) {
if (b)
return a + b;
else
return a;
}
let res1 = _add(1); // fine
let res2 = _add(1,2); // fine
let res3 = _add(1,2,3); // error
默认参数
对应js中的默认参数,ts也可以在函数声明时在同样位置定义默认参数,当没有值的时候取默认值,传递参数的时候替换默认值。
function _add(a: number, b=1) {
return a + b;
}
let res1 = _add(1); // 2
let res2 = _add(1, undefined); // 2
let res3 = _add(1, 2); // 3
看res1也可以看出带默认值的参数也是可选参数;之后来试试看默认参数能不能放在常规参数的前边
function _add(a=1, b: number) {
return a + b;
}
let res1 = _add(2); // error
let res2 = _add(undefined,2); // 3
let res3 = _add(2, 1); // 3
可以看出放在其他普通参数之前是可以的,并且传入参数数量仍需一致;
剩余参数
剩余参数也就是现在js中常用的rest参数,通常(…rest)一丢就完了,或者麻烦一点使用arguments进行遍历;
在ts里也可以用…rest收集参数
function _add(a: number, ...rest: number[]) {
return a+rest.reduce(function (x, y) { return x + y })
}
console.log(_add(1,2,3,4,5)) //15
当然rest这个是可以随意命名的,rest只是个习惯。
重载
重载是指多个函数接受不同的参数而不同处理返回的情况;JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。但是es中如果定义多个重名function则代码后面的会将之前的同名函数覆盖。ts参考了java\c#等面向对象语言的习惯,也搞了个重载
function fun(arg: string): string
function fun(arg: number): number
function fun(arg:any):any{
if(typeof arg === 'string'){
return '字符串';
}else if(typeof arg === 'number'){
return ++arg
}
}
所示情况是传入参数类型不一致,另一种情况就是参数数量不一致的
function fun(arg: string): string
function fun(arg: string,sec:number): number
function fun(arg:any,sec?:any):any{
if(sec){
return arg+sec;
}else{
return arg;
}
}
this
我大致扫了一眼文档觉得和js的this没有啥冲突,所以以下是照搬文档了,已备遇到问题再翻过头来看,自信的可以跳过了。
this和箭头函数
JavaScript里,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);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
可以看到createCardPicker是个函数,并且它又返回了一个函数。 如果我们尝试运行这个程序,会发现它并没有弹出对话框而是报错了。 因为 createCardPicker返回的函数里的this被设置成了window而不是deck对象。 因为我们只是独立的调用了 cardPicker()。 顶级的非方法式调用会将 this视为window。 (注意:在严格模式下, this为undefined而不是window)。
为了解决这个问题,我们可以在函数被返回时就绑好正确的this。 这样的话,无论之后怎么使用它,都会引用绑定的‘deck’对象。 我们需要改变函数表达式来使用ECMAScript 6箭头语法。 箭头函数能保存函数创建时的 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 here
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
更好事情是,TypeScript会警告你犯了一个错误,如果你给编译器设置了–noImplicitThis标记。 它会指出 this.suits[pickedSuit]里的this的类型为any。
this参数
不幸的是,this.suits[pickedSuit]的类型依旧为any。 这是因为 this来自对象字面量里的函数表达式。 修改的方法是,提供一个显式的 this参数。 this参数是个假的参数,它出现在参数列表的最前面:
function f(this: void) {
// make sure `this` is unusable in this standalone function
}
让我们往例子里添加一些接口,Card 和 Deck,让类型重用能够变得清晰简单些:
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
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 Deck
createCardPicker: 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};
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
现在TypeScript知道createCardPicker期望在某个Deck对象上调用。 也就是说 this是Deck类型的,而非any,因此–noImplicitThis不会报错了。
this参数在回调函数里
你可以也看到过在回调函数里的this报错,当你将一个函数传递到某个库函数里稍后会被调用时。 因为当回调被调用的时候,它们会被当成一个普通函数调用, this将为undefined。 稍做改动,你就可以通过 this参数来避免错误。 首先,库函数的作者要指定 this的类型:
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
this: void means that addClickListener expects onclick to be a function that does not require a this type. Second, annotate your calling code with this:
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// oops, used this here. using this callback would crash at runtime
this.info = e.message;
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!
指定了this类型后,你显式声明onClickBad必须在Handler的实例上调用。 然后TypeScript会检测到 addClickListener要求函数带有this: void。 改变 this类型来修复这个错误:
class Handler {
info: string;
onClickGood(this: void, e: Event) {
// can't use this here because it's of type void!
console.log('clicked!');
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickGood);
因为onClickGood指定了this类型为void,因此传递addClickListener是合法的。 当然了,这也意味着不能使用 this.info. 如果你两者都想要,你不得不使用箭头函数了:
class Handler {
info: string;
onClickGood = (e: Event) => { this.info = e.message }
}
这是可行的因为箭头函数不会捕获this,所以你总是可以把它们传给期望this: void的函数。 缺点是每个 Handler对象都会创建一个箭头函数。 另一方面,方法只会被创建一次,添加到 Handler的原型链上。 它们在不同 Handler对象间是共享的。