Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript - Functions(函数类型) #26

Open
wozien opened this issue Nov 2, 2020 · 0 comments
Open

TypeScript - Functions(函数类型) #26

wozien opened this issue Nov 2, 2020 · 0 comments

Comments

@wozien
Copy link
Owner

wozien commented Nov 2, 2020

函数中的类型注解

函数可以指定每个参数和返回值的类型:

function add(x: number, y: number): number {
  return x + y;
}

let myAdd = function (x: number, y: number): number {
  return x + y;
};

当省略返回值的类型注解,Typescript 会自动根据返回的值进行类型推断。

函数类型

像其他变量的注解一样,也可以对一个变量标注为函数类型,格式为 (a: number, b:number) => number, 如下:

let myAdd: (x: number, y: number) => number = function (
  x: number,
  y: number
): number {
  return x + y;
};

注意 xy 只是提供占位功能,可以任意取名

类型推断

可以只写等式一边的类型注解,其余一边 Typescript 会自动根据语言环境自动进行类型推断:

// The parameters 'x' and 'y' have the type number
let myAdd = function (x: number, y: number): number {
  return x + y;
};

// myAdd has the full function type
let myAdd2: (baseValue: number, increment: number) => number = function (x, y) {
  return x + y;
};

可选和默认参数

Typescript 中函数的参数默认是都是必须的,所以在函数调用时传递参数的数量必须和声明时要相同:

function buildName(firstName: string, lastName: string) {
  return firstName + " " + lastName;
}

let result1 = buildName("Bob"); // error, too few parameters
Expected 2 arguments, but got 1.
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
Expected 2 arguments, but got 3.
let result3 = buildName("Bob", "Adams"); // ah, just right

JavaScript 中,函数的每个参数都是可选的,用户可以根据情况传递,没有传参的值默认为 undefined。而在 TypeScript 中,要想让一个参数可选,可以把类型注解改为 ?:

function buildName(firstName: string, lastName?: string) {
  if (lastName) return firstName + " " + lastName;
  else return firstName;
}

let result1 = buildName("Bob"); // works correctly now
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
Expected 1-2 arguments, but got 3.
let result3 = buildName("Bob", "Adams"); // ah, just right

注意的是,可选参数需要跟在必录参数后面。另外,我们可以为一个参数提供默认值,在用户没传或者传 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
Expected 1-2 arguments, but got 3.
let result4 = buildName("Bob", "Adams"); // ah, just right

跟随在必录参数后面的可选参数和默认参数可以看成是等价的:

function buildName(firstName: string, lastName?: string) {
  // ...
}

等于:

function buildName(firstName: string, lastName = "Smith") {
  // ...
}

和可选参数不一样的是,默认参数可以在必录参数前面出现。这个时候需要使用默认值,必须显示传递 undefiend 值:

function buildName(firstName = "Will", lastName: string) {
  return firstName + " " + lastName;
}

let result1 = buildName("Bob"); // error, too few parameters
Expected 2 arguments, but got 1.
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
Expected 2 arguments, but got 3.
let result3 = buildName("Bob", "Adams"); // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"

剩余参数

ES6 中的剩余参数类似,用 ... 表示剩余参数,它可以看出把所有的可选参数都放进一个数组:

function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

// employeeName will be "Joseph Samuel Lucas MacKinzie"
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;

函数重载

有一些函数的参数类型可以是多种类型的,并且返回类型也可以是不确定的:

let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: any): any {
  // Check to see if we're working with an object/array
  // if so, they gave us the deck and we'll pick the card
  if (typeof x == "object") {
    let pickedCard = Math.floor(Math.random() * x.length);
    return pickedCard;
  }
  // Otherwise just let them pick the card
  else 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);

上面函数如果传入一个对象,则返回一个 number。如果传入一个数字,则返回卡片对象。那在 TypeScript 中如何准确的描述这些不同参数的类型呢?那就是函数重载,它允许我们定义一系列不同情况的参数类型注解,并且一般遵循从具体到最模糊的顺序进行声明:

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): any {
  // Check to see if we're working with an object/array
  // if so, they gave us the deck and we'll pick the card
  if (typeof x == "object") {
    let pickedCard = Math.floor(Math.random() * x.length);
    return pickedCard;
  }
  // Otherwise just let them pick the card
  else 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);

在函数调用时, TypeScript 会从重载函数列表第一个开始进行类型匹配,如果参数匹配成功,就会根据这个声明进行类型检查。

注意的是,函数重载列表最后一个声明不会进行类型匹配,所以上面的列表如果参数不匹配前面2钟话,就会抛出异常

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant