1. // Parameter Type Annotations 参数类型的注释
  2. function greet(name: string) {
  3. console.log("Hello, " + name.toUpperCase() + "!!");
  4. }
  5. // Return Type Annotations 返回类型的注释
  6. function getFavoriteNumber(): number {
  7. return 26;
  8. }
  9. // Anonymous Functions 匿名函数
  10. const names = ["Alice", "Bob", "Eve"];
  11. // Contextual typing for function
  12. names.forEach(function (s) {
  13. console.log(s.toUppercase());
  14. // error: Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
  15. });
  16. // Contextual typing also applies to arrow functions
  17. names.forEach((s) => {
  18. console.log(s.toUppercase());
  19. // error: Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
  20. });

函数定义 Defining Functions

// 有名字的函数 Named function
function add(x, y) {
    return x + y;
}

// 匿名函数 Anonymous function
let myAdd = function(x, y) { return x + y; };

函数类型 Function Types

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

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

函数类型表达式 Function Type Expressions

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

调用签名 Call Signatures

函数有额外的属性:

type DescribableFunction = {
  description: string;
  (someArg: number): boolean;
};
function doSomething(fn: DescribableFunction) {
  console.log(fn.description + " returned " + fn(6));
}

构造签名 Construct Signatures

type SomeConstructor = {
  new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
  return new ctor("hello");
}

泛型函数 Generic Functions ✨

function firstElement<Type>(arr: Type[]): Type {
  return arr[0];
}
// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);

其中 Type 是没有明确定义的,而是推断(Inference)出来的。

我们也可以多个未明确的 Type

function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
  return arr.map(func);
}

// Parameter 'n' is of type 'number'
// 'parsed' is of type 'number[]'
const parsed = map(["1", "2", "3"], (n) => parseInt(n));

有时我们可能会需要限制 Type 类型参数,可以通过 extends 语句约束(Constraints) 类型:

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

// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString is of type 'string'
const longerString = longest("alice", "bob");
// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100);

在使用泛型约束的时候,很容易犯的错误:

function minimumLength<Type extends { length: number }>(
  obj: Type,
  minimum: number
): Type {
  if (obj.length >= minimum) {
    return obj;
  } else {
    return { length: minimum };
// Type '{ length: number; }' is not assignable to type 'Type'.
//   '{ length: number; }' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'.
  }
}

这段方法看起来OK,但是定义函数返回的是和传入参数 Type 一致的类型,约束值(Constrained Values)与约束的类型并非一致。

// 'arr' gets value { length: 6 }
const arr = minimumLength([1, 2, 3], 6);
// and crashes here because arrays have
// a 'slice' method, but not the returned object!
console.log(arr.slice(0));

泛型函数调用,一般可以推断出预期的类型,但也不一定如此:

function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
  return arr1.concat(arr2);
}

const arr = combine([1, 2, 3], ["hello"]);
// Type 'string' is not assignable to type 'number'.

当我们调用不一致类型时,会报错,但如果你执意要这样做,可以指定特定的类型参数(Specifying Type Arguments)

const arr = combine<string | number>([1, 2, 3], ["hello"]);

泛型函数书写指南

Guidelines for Writing Good Generic Functions:

  • Rule: When possible, use the type parameter itself rather than constraining it
  • Rule: Always use as few type parameters as possible
  • Rule: If a type parameter only appears in one location, strongly reconsider if you actually need it

1、尽量使用类型参数而不是限制类型

function firstElement1<Type>(arr: Type[]) {
  return arr[0];
}

function firstElement2<Type extends any[]>(arr: Type) {
  return arr[0];
}

// a: number (good)
const a = firstElement1([1, 2, 3]);
// b: any (bad)
const b = firstElement2([1, 2, 3]);

2、尽量使用更少的推断类型

function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {
  return arr.filter(func);
}

function filter2<Type, Func extends (arg: Type) => boolean>(
  arr: Type[],
  func: Func
): Type[] {
  return arr.filter(func);
}

3、如果类型参数在一个作用域只出现了一次,想一想是否真的需要使用泛型

function greet<Str extends string>(s: Str) {
  console.log("Hello, " + s);
}

greet("world");

上面的代码其实有更简单的写法:

function greet(s: string) {
  console.log("Hello, " + s);
}

函数调用 Calling a Function

Using Arguments

可选参数

在TypeScript里我们可以在参数名旁使用 ? 实现可选参数的功能。

function f(x?: number) {
  // ...
}
f(); // OK
f(10); // OK

即使我们定义了 x 类型为 number,实际上 x 类型可能为 number | undefined

默认参数

我们可以为参数提供默认值:

function f(x = 10) {
  // ...
}

剩余参数

this 参数

重载 Function Overloads

函数重载是指函数根据传入不同的参数,返回不同类型的数据。

function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);

上面的函数可以接受一个参数,也可以接受三个参数。上面的两个表达式被称为 overload signatures.

重载签名与实现签名

Overload Signatures and the Implementation Signature:
The signature of the implementation is not visible from the outside. When writing an overloaded function, you should always have two or more signatures above the implementation of the function.

这两者容易混淆,函数的实现并不在函数的外部;函数的重载,一般在函数实现上有一个或更多的定义。

如何写好重载

Writing Good Overloads

  • Always prefer parameters with union types instead of overloads when possible

1、尽可能使用联合类型取代函数重载

假设有这样一个函数,可以返回字符串或数组的长度:

function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
  return x.length;
}

我们可以调用字符串或数组,但是不能调用字符串或数字这样不确定的入参,因为重载只能符合一种表达式:

len(""); // OK
len([0]); // OK
len(Math.random() > 0.5 ? "hello" : [0]);
/** 
No overload matches this call.
  Overload 1 of 2, '(s: string): number', gave the following error.
    Argument of type 'number[] | "hello"' is not assignable to parameter of type 'string'.
      Type 'number[]' is not assignable to type 'string'.
  Overload 2 of 2, '(arr: any[]): number', gave the following error.
    Argument of type 'number[] | "hello"' is not assignable to parameter of type 'any[]'.
      Type 'string' is not assignable to type 'any[]'.
**/

我们可以用一种非重载的方式来写:

function len(x: any[] | string) {
  return x.length;
}