# 函数类型

typescript 中的函数类型声明

# 函数类型声明

function greeter(fn: (a: string) => void) {
  fn("Hello, World");
}

function printToConsole(s: string) {
  console.log(s);
}

greeter 中的 fn 的类型可以提取出来

type GreetFunction = (a: string) => void;
function greeter(fn: GreetFunction) {
  // ...
}

# 函数签名

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

# 构造函数类型

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

当方法既可以作为构造函数又可以作为普通函数时

interface CallOrConstruct {
  new (s: string): Date;
  (n?: number): number;
}

# 泛型函数

在 TypeScript 中,当我们想要描述两个值之间的对应关系时,就会使用泛型。我们可以通过在函数签名中声明类型形参来实现:

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

# 匿名函数

首先要先理解,当前的类型指的是函数本身。

type Ianonymity<T> = (input: T) => T;

const func: Ianonymity<number> = (input) => {
  return input + input;
};

# 匿名函数(泛型)

感觉上面的理解还是别扭

type Ianonymity = <T>(input: T) => T;

const func: Ianonymity = (input) => {
  return input + input;
};

# 推断

很多时候 ts 可以根据我们实际入参的类型来推断出实际的类型,而不需要类型注解。如下例:

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

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

InputOutput 都能够推断出来, Inputstring , Outputnumber

# 约束

泛型传入函数后,终究是要处理的,当我们需要传入的类型具有某种共同的特性时,我们就需要通过 extends 来对泛型进行约束。

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

这样一来 Type 类型继承于 {length: number}, 当我们在代码中调用参数时,可以避免错误。

# 指定类型参数

假设有这么一个方法

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

我们想这样用

const arr = combine([1, 2, 3], ["hello"]);

很遗憾,会报错,因为根据 [1,2,3] 会推断出 Typenumber, 如果想用的话,我们可以显示的指定类型

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

# 编写良好泛型函数的指导原则

  • 向下推类型参数 能指定 <Type> 就不要指定 <Type[]>
  • 使用更少的类型参数(在指定类型参数时,要尽量的少)
  • 类型参数应该出现两次(是否真的需要泛型)

# 可选参数

我们可以在 TypeScript 中用 ? 标记参数为可选:

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

我们也可以提供一个默认值

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

# 函数重载

虽然实现方式还是 javascript 的实现方式,但是在类型的检测和提示上,还是提供了很大的帮助。

function getInfo(name: string): void;
function getInfo(age: number): void;
function getInfo(str: any): void {
  if (typeof str == "string") {
    console.log("名字:", str);
  } else if (typeof str == "number") {
    console.log("年龄", str);
  } else {
    console.log("这是个什么玩意儿", any);
  }
}
getInfo("zhangsan");

要好好考虑一下重载的意义的。。

#this 指定类型

function 中(注意不能使用箭头函数,因为箭头函数没有自己的 this)的第一个参数指定 this

const admins = db.filterUsers(function (this: User) {
  return this.admin;
});

# 在函数中声明 this

在 js 中对于 this 的调用时非常灵活的,我们可以通过 call bind apply 等方式更改 this。能够指定 this 的类型,相当有必要

const user = {
  id: 123,

  admin: false,
  becomeAdmin: function () {
    this.admin = true;
  },
};

上面例子中, this 指的是 user 对象,那么在 typescript 中如何指定 this

interface DB {
  filterUsers(filter: (this: User) => boolean): User[];
}

const db = getDB();
const admins = db.filterUsers(function (this: User) {
  return this.admin;
});

# 剩余参数

在这里除了类型的定义外,与 es6 是一致的。我们关注一下类型的定义:

const f = (name: string, age: number) => {
  console.log(name, age);
};

// const f: (name: string, age: number) => void

const t = (...args: [string, number]) => {
  console.log(...args);
};

// const t: (args_0: string, args_1: number) => void

const p = ["sss", 20] as const;

t(...p);
f(...p);

在实用工具中有一个 Parameters<Type> 可以活动函数类型的参数定义,得到的结果就是元组,那么用元组直接定义函数类型也是可以的

# 参数解构

参考 es6, 只是参数的类型,也可以解构

function sum({ a, b, c }: { a: number; b: number; c: number }) {
  console.log(a + b + c);
}