函数

TypeScript 函数

函数类型在 TypeScript 类型系统中扮演着非常重要的角色,它们是可组合系统的核心构建块。

参数注解

TypeScript 中函数的声明与 JavaScript 中保持一致,不过其允许指定额外的类型信息:

let stories: [string, string[]][] = [];

function addStory(title: string, tags: string[]): void {
  stories.push([title, tags]);
}

同样可以在 Lambda 表达式中指定类型:

let sortByLength: (x: string, y: string) => number = (x, y) =>
  x.length - y.length;
tags.sort(sortByLength);

也可以在函数参数中指定可选参数:

function storySummary(title: string, description?: string) {
  if (description) {
    return title + description;
  } else {
    return title;
  }
}

或者使用默认值:

function storySummary(title: string, description: string = "") {
  return title + description;
}

值得一提的是,当我们确定某个函数并不返回值时,需要注意不能使用 any 来替代 void,以避免误用返回值的情形:

function fn(x: () => void) {
  const k = x(); // oops! meant to do something else
  k.doSomething(); // error, but would be OK if the return type had been 'any'
}

函数重载

JavaScript 中并不支持函数重载,但是在 TypeScript 中我们可以通过参数的不同实现重载:

declare function createStore(
  reducer: Reducer,
  preloadedState: PreloadedState,
  enhancer: Enhancer
);
declare function createStore(reducer: Reducer, enhancer: Enhancer);

该特性可以帮我们优化代码,譬如:

function padding(a: number, b?: number, c?: number, d?: any) {
  if (b === undefined && c === undefined && d === undefined) {
    b = c = d = a;
  } else if (c === undefined && d === undefined) {
    c = a;
    d = b;
  }
  return {
    top: a,
    right: b,
    bottom: c,
    left: d
  };
}

如果仔细查看代码,就会发现 a,b,c,d 的值会根据传入的参数数量而变化。此函数也只需要 1 个,2 个或 4 个参数。可以使用函数重载来强制和记录这些约束。你只需多次声明函数头。最后一个函数头是在函数体内实际处于活动状态但不可用于外部。

// 重载
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
// Actual implementation that is a true representation of all the cases the function body needs to handle
function padding(a: number, b?: number, c?: number, d?: number) {
  if (b === undefined && c === undefined && d === undefined) {
    b = c = d = a;
  } else if (c === undefined && d === undefined) {
    c = a;
    d = b;
  }
  return {
    top: a,
    right: b,
    bottom: c,
    left: d
  };
}

padding(1); // Okay: all
padding(1, 1); // Okay: topAndBottom, leftAndRight
padding(1, 1, 1, 1); // Okay: top, right, bottom, left

padding(1, 1, 1); // Error: Not a part of the available overloads

TypeScript 中的函数重载没有任何运行时开销。它只允许你记录希望调用函数的方式,并且编译器会检查其余代码。

函数类型注解

我们可以使用类型别名或者接口来表示一个可被调用的类型注解:

interface ReturnString {
  (): string;
}

它可以表示一个返回值为 string 的函数:

declare const foo: ReturnString;

const bar = foo(); // bar 被推断为一个字符串。

一个接口可提供多种调用签名,用以特殊的函数重载:

interface Overloaded {
  (foo: string): string;
  (foo: number): number;
}

// 实现接口的一个例子:
function stringOrNumber(foo: number): number;
function stringOrNumber(foo: string): string;
function stringOrNumber(foo: any): any {
  if (typeof foo === 'number') {
    return foo * foo;
  } else if (typeof foo === 'string') {
    return `hello ${foo}`;
  }
}

const overloaded: Overloaded = stringOrNumber;

// 使用
const str = overloaded(''); // str 被推断为 'string'
const num = overloaded(123); // num 被推断为 'number'
这也可以用于内联注解中:

let overloaded: {
  (foo: string): string;
  (foo: number): number;
};

Generator | 生成器

function* numbers(): IterableIterator<number> {
  console.log("Inside numbers; start");
  yield 1;
  console.log("Inside numbers; after the first yield");
  yield 2;
  console.log("Inside numbers; end");
}
// 迭代器结果的类型声明
interface IteratorResult<CompletedType, SuspendedType> {
  value: CompletedType | SuspendedType;
  done: this is { value: CompletedType };
}
上一页
下一页