# 函数签名

# 函数类型表达式

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

function printToConsole(s: string) {
  console.log(s);
}
greeter(printToConsole);
1
2
3
4
5
6
7
8
type GreetFunction = (a: string) => void;

function greeter(fn: GreetFunction) {
  // ...
  fn("Hello, World");

}

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



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 调用签名

type Log = {
  tag: string;
  (name:string): void;
}

function say(name) {
  console.log('object :>> ', name);
}
say.tag = '玩吧'
function pluLog(fn: Log) {

  return fn.tag + fn('erer')
}
pluLog(say)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 构造函数签名

可以通过在调用签名之前添加new关键字来编写构造签名

export interface VueConstructor<V extends Vue = Vue> {
  new <Data = object, Methods = object, Computed = object, PropNames extends string = never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): CombinedVueInstance<V, Data, Methods, Computed, Record<PropNames, any>>;
  // ideally, the return type should just contain Props, not Record<keyof Props, any>. But TS requires to have Base constructors with the same return type.
  new <Data = object, Methods = object, Computed = object, Props = object>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): CombinedVueInstance<V, Data, Methods, Computed, Record<keyof Props, any>>;
  new (options?: ComponentOptions<V>): CombinedVueInstance<V, object, object, object, Record<keyof object, any>>;
}

1
2
3
4
5
6
7

声明像 Date 一样的构造器,既可以new 也可以直接调用

interface CallOrConstruct {
  new (s: string): Date;
  (n?: number): number;
}
1
2
3
4

# 泛型函数

不好的实例


function firstElement(arr: any[]) {
  return arr[0];
}

1
2
3
4
5

好的实例



function firstElement<T>(arr: T[]):T {
  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]);



1
2
3
4
5
6
7
8
9
10
11
12

在泛型中,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));
1
2
3
4
5
6
7

# 类型约束

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");
1
2
3
4
5
6
7
8
9
10
11
12
function minimumLength<Type extends { length: number }>(
  obj: Type,
  minimum: number
): Type {
  if (obj.length >= minimum) {
    return obj;
  } else {
    return { length: minimum };

    // 出错了,返回的不是泛型

    return { length: minimum } as Type;

  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 指定类型参数

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

// const arr = combine([1, 2, 3], ["hello"]); // 错误了❌

const arr = combine<string | number>([1, 2, 3], ["hello"])
1
2
3
4
5
6
7

# 编写泛型函数的准则

  • Push Type Parameters Down
  • 使用较少的类型参数
  • 类型参数应该至少出现两次

# 可选参数 ?

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

1
2
3
4
5
6

回调函数的可选参数

function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i], i);
  }
}
1
2
3
4
5

# 函数重载

我们写了两个函数重载签名,又写了一个函数,这个函数有自己的实现签名,尽管这个实现签名又两个可选函数,但是还是不能直接调用

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);
const d3 = makeDate(5, 5);


1
2
3
4
5
6
7
8
9
10
11
12
13
14

实现签名还必须与重载签名兼容。

function fn(x: string): string;
// Return type isn't right
function fn(x: number): boolean;
function fn(x: string | number):string |boolean {
  return "oops";
}
1
2
3
4
5
6

# 如何写好重载

# 好的案例

实现一个函数,返回字符串或者数组的长度

function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
  return x.length;
}
1
2
3
4
5

不用签名直接写参数

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

# 需要知道的其他类型

  • void - void 和 undefined不同
  • object - object 不是 Object.使用 object!
  • unknown - 未知类型表示任何值,和any类似,但是更安全.当我们对unknown进行操作的时候
function f1(a: any) {
  a.b(); // OK
}
function f2(a: unknown) {
  a.b();
//   ❌ Object is of type 'unknown'.
}

function safeParse(s: string): unknown {
  return JSON.parse(s);
}

// Need to be careful with 'obj'!
const obj = safeParse(JSON.stringify({name:1}));

console.log('obj.name :>> ', obj.name);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • never 永远不会有返回值的函数,这意味着该函数将引发异常或终止程序的执行。
function fail(msg: string): never {
  throw new Error(msg);
}
1
2
3
  • Function - 使用 () => void 代替

# 剩余参数

In TypeScript, the type annotation on these parameters is implicitly any[] instead of any, and any type annotation given must be of the form Array<T>or T[], or a tuple type (which we’ll learn about later).

function multiply(n: number, ...m: number[]) {
  return m.map((x) => n * x);
}
// 'a' gets value [10, 20, 30, 40]
const a = multiply(10, 1, 2, 3, 4);



const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);



const args = [8, 5] as const;
// OK
const angle = Math.atan2(...args);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 参数解构类型声明

对象的类型注释在解构语法之后:

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

// 解构加类型声明
function sum({ a, b, c }: { a: number; b: number; c: number }) {
  console.log(a + b + c);
}

// 使用类型别名
type ABC = { a: number; b: number; c: number };
function sum({ a, b, c }: ABC) {
  console.log(a + b + c);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15