TypeScript 中如何声明函数?

函数(在对象中又叫做方法),是编码中最普遍存在和使用的一种对象。本节学习如何在 TypeScript 中声明函数,方便我们开发。

下面将从以下 6 个方面来讲:类型表达式、调用签名、构造签名、函数重载、泛型函数、可选参数。

一、类型表达式(Function Type Expressions)

函数类型表达式(Function Type Expressions)是最简单声明函数的方式。语法类似 (s: string) => void,使用胖箭头(=>)分隔参数列表和返回值类型。

function greeter(fn: (s: string) => void{
  fn('Hello Wolrd');
}

二、调用签名(Call Signatures)

这是为了满足声明那些包含属性的函数。JavaScript 中函数就是一类特殊的对象。

比如我们有一个函数 doSomething,接收一个函数类型参数 fnfn 类型如下:

  1. 接收一个数值类型 number 的参数调用,返回布尔类型
  2. 还包含一个字符串类型属性 description
type CallableFunc = {
  decription: string;
  (arg: number): boolean
}

function doSomething(fn: CallableFunc{
  fn.decription;
  fn(123);
}

注意,这里声明函数使用的是冒号 : 而非胖箭头 =>,对象类型中是采用 k: v 形式指定键值对的。

三、构造签名(Construct Signatures)

构造函数声明类似函数调用声明,不过多了一个 new 前缀。

type Ctor = {
  new (s: string): Date;
}

function fn(ctor: Ctor{
  new ctor('hello');
}

当然,调用签名和构造签名可以同时使用,

type Ctor = {
  new (s: string): Date;
  (n: number): Date;
}

function fn(ctor: Ctor{
  new ctor('hello');
  ctor(12315);
}

四、函数重载(Function Overloads)

有时某个函数可能要支持不同参数的调用形式。假设有一个函数 makeDate() 来说:

  • 既支持通过时间戳(number 类似)来创建日期对象
  • 也支持通过月(m)、日(d)、年(y)来创建日期对象

如何定义这个 makeDate() 函数?

// Error: Duplicate function implementation.ts(2393)
function makeDate(timestamp: number): Date {}
function makeDate(m: number, d: number, y: number): Date {}

这时候就要用到函数重载了。

function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrtimestamp: number, d?: number, y?: number{
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrtimestamp, d);
  }

  return new Date(mOrtimestamp);
}

五、泛型函数(Generic Functions)

设想这样一个场景,我有一个函数,获取数组的第一个元素。返回值类型取决于传入的数组。

比如:传入的是 number[] 类型,那么返回 number;传入的是 string[] 类型,那么返回 string

我们可以用函数重载来实现:

function firstElem(arr: number[]): number | undefined;
function firstElem(arr: string[]): string | undefined;
function firstElem(arr: number[] | string[]): string | number | undefined {
  return arr[0];
}

firstElem([1234]);
firstElem(['1''2''3''4']);

但不够灵活。因为返回值类型取决于传入的数组,所以我们可以抽象出一个泛型 T 来表示数组元素类型。

function firstElem2<T>(arr: T[]): T | undefined {
  return arr[0];
}

firstElem2([1234]);
firstElem2(['1''2''3''4']);
firstElem2([truefalse]);

泛型 T 是一种特殊的类型,依赖实际调用场景。函数中泛型的声明放在函数名之后,用尖括号 <> 包裹。

对应到本例,我们声明了一个函数 firstElem2,其接收一个元素类型为“T”的数组,函数返回值类型关联数组元素类型——返回 T(有元素) 或者 undefined(无元素)。

当我们使用 [1, 2, 3, 4] 调用 firstElem2() 函数时,TypeScript 推导泛型 T 即是 number 类型,继而推导返回类型为 number | undefiend。后续使用 ['1', '2', '3', '4'] 以及 [true, false] 调用,原理类似。

使用泛型可以简化我们的函数类型声明。

当然,还能「同时使用多个泛型、对泛型类型做约束」

  1. 使用多个泛型

泛型间使用逗号隔开。

function map<InputOutput>(arr: Input[], fn: (input: Input) => Output): Output[] {
  return arr.map(fn);
}

map([1,2,3], (num) => num * 2);
map([1,2,3], (num) => `${ num }`);
  1. 对泛型类型做约束

使用 extends 关键字来约束泛型范围。

function longest<T extends { length: number }>(a: T, b: T) {
  if (a.length >= b.length) {
    return a;
  } else {
    return b;
  }
}

longest([1], [23]);
longest('1''2');
// Error: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.
// longest(1, 2);

六、可选参数(Optional Parameters)

与在 interface 中声明对象类型的可选属性类似,可选参数也是使用的 ?:

// 必填参数
function f(num: number{}

// Error: Expected 1 arguments, but got 0.
// f();
f(1);

// 可选参数
function f2(num?: number{
  // Error: 'num' is possibly 'undefined'.
  // num.toFixed();
}

f2();
f2(1);

// 参数默认值
function f3(num = 10{
  num.toFixed();
}


原文始发于微信公众号(写代码的宝哥):TypeScript 中如何声明函数?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/244045.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!