Typescript杂谈(一)


函数

函数类型

函数类型包含两部分:类型参数返回值类型。完整函数类型这两部分都是需要的

let myAdd: (baseValue:number,increament:number) => number =
function (x:number,y:number):number{
	return x + y
}
// *********************解释说明*********************
/**
* 定义字面量函数
* myAdd: 变量名为函数名
* (baseValue:number,increament:number):number => number 为函数类型
* function(){// dosomething } 为函数体
*/

可选参数、默认参数和剩余参数

  1. 可选参数
    在形参后面添加?
function buildName(firstName:string, lastName?:string){
	if(lastName){
		return firstName + " " +lastName
	}else{
		return firstName
	}	
}
  1. 可选参数 一般位于形参列表的最后一个,可传可不传,如果不传则值就是undefined。
  1. 默认初始化值的参数
    我们可以为参数提供一个默认初始值当用户没有传递这个参数或者传递的值是undefined时,它们叫做有默认初始化值得参数
function bulidName(firstName: string, lastName = "Lucky"){
	return firstName +  " " + lastName;
}
buildName("Bob") //	 works correctly now
buildName("Bob", undefinded) //	still works, also returns
buildName("Bob", "Adams", 'Sr') // error, to many parameters
buildName("Bob", "Lucky") //	ah, just right

所有必须参数后面的带默认初始化的参数都是可选的,与可选参数一样,在调用函数的时候可以省略。也就是说可选参数与末尾的默认参数共享数据类型

function bulidName(firstName:string,lastName?:string){
 return ``
}


function bulidName(firstName:string,lastName?:string){
 return ``
}
//	共享同样得数据类型
(firstName:sring, lastName?:string) => string
  1. 在函数类型类型中,默认参数的默认值是不会显示,而只会显示它是一个可选参数
  2. 与普通的可选参数不同的是,带有默认值的参数不需要放在必须参数后面。如果带默认值的参数出现在必选参数前面,用户必须明确的传入undefined值来获得默认值。
function bulidName(firstName = "Lukcy", lastName:string){
	return firstName + " " + lastName
}
buildName("Bob");	//	error,too few parameters
buildName("Bob", "Adams", "Sr"); 	//	error, to many parameters
buildName("Bob", "Adams") 	//	okay and returns "Bob Adams"
bulidName(undefinded,"Adams")	//	okay and returns "Will Adams"
  1. 剩余参数
    必要参数,默认参数和可选参数有个共同点:它们表示一个参数。有时,想同时操作多个参数,或许并不知道会有多少个参数传递进来。
  • 在Typescript里,可以把所有参数收集到一个变量里:
	function bulidName(firstName:string, ...restOfName:string[]){
		return firstName + " " + restOName.join(" ")
	}
	bulidName("Lukcy", "YAN", "YEN")
  • 剩余参数或被当做不限个数的可选参数。可以一个都没有也可以有任意个。编译器创建参数数组,名字是在省略号(...)后面给定的名字,你可以在函数体内使用这个数组
function bulidName(firstName:string, ...restOfName:string[]){
	return firstName + " " + restOfName.join(" ")
}

  • 这个省略号也会在带有剩余参数的函数类型定义上使用:
    (fisrtName:string, ...restOfName:string[]) => string




关于this的补充

在JavaScript中当前函数的this是在函数调用执行的时候才确定的

  1. 全局this
    在全局对象中this指向本身
  2. 函数中的this
    在一个函数执行上下文中,this由该函数的调用者提供,由调用函数的方式来决定this指向

如果调用者被某一个对象所拥有,那么在调用该函数时,内部this指向该对象。
如果调用者函数独立调用,那么改函数内部的this则指向undefined。
但是在非严格模式中,当this指向undefined时,它会自动指向全局。

function fn(){
	//	TodoSomething
}
fn() //		fn为调用者
  1. call、apply和bind
  • JavaScript内部提供了一种可以手动设置内部this指向的方式,它们就是call/apply/bind。所有的函数都能调用这三个方法。
  • 当函数调用call/apply时,则表示会执行该函数,并且函数内部的this指向call/apply的第一个参数。
  • 而call/apply的不同之处至于参数的传递形式,
    • call 的第一个参数是为函数内部指定this指向,后续的参数则是函数执行时所需要的参数,一个个传递。
    • apply 的第一个参数与call 相同,为函数内部this 指向,而函数的参数,则以数组的形式传递,作为apply 的第二个参数。
    • 当函数调用call/appy 时,函数的内部this 被显式指定,并且函数会立即执行。
      而当函数调用bind 时,函数并不会立即执行,而是返回一个新的函数,这个新的函数与原函数有共同的函数体,
      但它并非原函数,并且新函数的参数与this 指向都已经被绑定,参数为bind 的后续参数。
function fn(num1,num2){
	return this.a + num1 + num2;
}
var a = 20;
var object = {
	a: 40
}
var _fn = fn.bind(object,1,2);
console.log(_fn === fn); // false
_fn();	// 43
_fn(1,4);	//	43 因为参数被绑定,因此重新传入参数是无效的




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 {
        // "this" 隐式具有类型 "any",因为它没有类型注释。
        suit: this.suits[pickedSuit],
        card: pickedCard % 13,
      };
    };
  },
};

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

this 参数

上面例子中的this.suits[pickedSuit]中的this的类型依然为any.这是因为this来自对象字面量里的函数表达式
修改的方法是,提供一个显示的this参数。this参数是个假的参数

function f(this:void){
	//	make sure `this` is unusable in this distance function
}
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),
  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);

回调函数中的this

当你将一个函数传递到某个库函数里在稍后被调用时,你可能也见到过回调函数里的this会报错。因为当回调函数被调用时,它会被当成普通函数调用,this将为undefined

interface UIElement {
  addClickListener(onclick: (this: void, e: Event) => void): void;
}
/**
 * this:void 意味着 addClickListener 期望onclick是一个函数且它不需要一个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 ......

泛型

在泛型约束中使用类型参数

可以声明一个类型参数,且它被另一个类型参数所约束。比如想要用属性名从对象中获取这个属性。且确保这个属性在对象上存在,需要在这两个类型之间使用约束。

function getProperty(obj: T, key: K) {
  return obj[key];
}

let x = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
};
console.log(getProperty(x, "a"));
console.log(getProperty(x, "m")); 
//  Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'. 

在泛型里使用类类型

function create(c: { new (): T }): T {
  return new c();
}

一个更高级的例子,使用原型属性推断并约束构造函数与类实例的关系

class BeeKeeper {
  hasMask: boolean;
}
class ZooKeeper {
  nameTag: string;
}
class Animal {
  nemLegs: number;
}
class Bee extends Animal {
  keeper: BeeKeeper;
}
class Lion extends Animal {
  keeper: ZooKeeper;
}
function createInstance(c: new () => A): A {
  return new c();
}

createInstance(Lion).keeper.nameTag // typechecks
createInstance(Bee).keeper.hasMask // typechecks