Menu

TypeScript 函数:参数类型、返回类型与 void

如何在 TypeScript 中给函数标注类型:为每个参数加上标注,标注或推断返回类型,用 void 表示不返回任何值的函数,以及为箭头函数、函数表达式、async 函数和 this 参数标注类型。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

TypeScript 函数就是给参数加上类型、并可选地给返回值加上类型的 JavaScript 函数。每个参数都有一个标注,返回类型写在参数列表之后,编译器会按这两者检查每一次调用。

两个错误的调用都是编译期错误(TS2345 和 TS2554)。// @ts-expect-error 注释告诉编译器下一行预期会出错,所以文件的其余部分仍然能运行。删掉其中一条再运行一次,就能看到真实的报错信息。

参数类型

每个参数都写成 name: Type。在 strict 下(TypeScript 7 默认开启),如果一个参数既没有标注,又没有可供推断的上下文,就会报错:Parameter 'x' implicitly has an 'any' type.(TS7006)。所以实际上,独立函数的每个参数都要加标注。

任何类型都可以作为参数类型:基本类型、数组、对象类型、联合类型,以及其他函数。

TypeScript 也会检查参数的个数。传入的实参比函数声明的多或少都会报错,这和普通 JavaScript 不同,后者会用 undefined 填补缺少的参数并忽略多余的参数。要让参数变成可选的或给它一个默认值,见可选参数。

返回类型:标注还是推断

返回类型跟在右括号之后:function f(): Type。它是可选的。没有标注时,TypeScript 会根据函数体中的每一个 return 推断类型。

当函数被导出、有多条返回路径或者是递归函数时,标注就很有价值。这样错误会出现在函数内部那个错误的 return 处,而不是出现在调用方。一个典型的例子是某个分支忘了返回:

编译器报告 index.ts(2,32): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.。在最后加上 return "C"; 就能运行。如果没有 : string 标注,这个函数会以推断出的类型 "A" | "B" | undefined 通过编译,问题就转移到了使用结果的地方。

void:不返回任何值的函数

只负责做事(打印日志、写入数据、修改状态)的函数,返回类型是 void。没有带值的 return 时,TypeScript 推断出的就是它,你也可以显式写出来。

关于 void 的这些规则常常让人意外:

情况是否允许?
function f(): void { return 42; }否:Type 'number' is not assignable to type 'void'.(TS2322)
const f: () => void = () => 42;是:值会被返回,但调用方不能依赖它
function f(): undefined {}是(TypeScript 5.1 及以后)
在 if 中使用 void 函数的结果否:An expression of type 'void' cannot be tested for truthiness.(TS1345)

第二行是有意为之的。正因如此,即使 push 返回一个数字,arr.forEach(x => list.push(x)) 也能通过编译。函数类型中的 void 意思是“不管它返回什么,都没人会读”。对于永远不会返回的函数(总是抛出错误或无限循环),返回类型是 never。

箭头函数与函数表达式

箭头函数和函数表达式使用同样的标注。返回类型写在参数列表之后、=> 之前。

如果没有括号,(x, y) => { x, y } 就是一个没有 return 的代码块,函数返回 undefined。箭头函数的运行时规则(没有自己的 this,没有 arguments)和 JavaScript 中一样;见 JavaScript 中的箭头函数。

当函数作为实参内联书写时,通常完全不需要标注它的参数。TypeScript 能从接收它的函数知道回调会收到什么(上下文类型推断):在 [1, 2].map(n => n * 2) 中,n 已经是 number。把函数的类型写成一个值的类型,即 (n: number) => string,见函数类型。

返回多个值

函数只能返回一个值,所以返回一个对象或元组,再进行解构。

元组返回类型需要标注。没有标注时,[a, b] 会被推断为 string[]:解构仍然可以工作,但类型不再说明恰好有两个元素。

async 函数

async 函数总是返回一个 Promise。把返回类型标注为 Promise<T>,其中 T 是函数内部返回的值的类型。

async function loadScore(id: number): Promise<number> {
  await new Promise((resolve) => setTimeout(resolve, 10));
  return id * 10;
}

async function main(): Promise<void> {
  const score = await loadScore(4); // score: number
  console.log("score", score);
}

main();

写成 async function f(): number 会报错:The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'?(TS1064)。

this 参数

JavaScript 在调用时才决定 this。TypeScript 允许你用一个名为 this 的假的第一个参数,来声明 this 必须是什么。它会从编译后的 JavaScript 中删除,调用方也不需要传入它。

interface Counter {
  count: number;
}

function increment(this: Counter, by: number): void {
  this.count += by;
}

const c = { count: 0, increment };
c.increment(2); // fine: this is c

increment(2);
// error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Counter'.

如果没有 this 参数,独立函数中的 this 就是隐式的 any,严格模式会报告 'this' implicitly has type 'any' because it does not have a type annotation.(TS2683)。在类方法中,this 的类型已经是类的实例,所以这个参数主要用于挂在对象上的独立函数,以及库会用特定 this 调用的回调。

常见问题

TypeScript 中如何指定函数的返回类型?

在参数列表后面写一个冒号和类型:function total(a: number, b: number): number { ... }。箭头函数也写在同样的位置:const total = (a: number, b: number): number => a + b;。如果省略,TypeScript 会根据 return 语句推断返回类型。

TypeScript 中总是要标注返回类型吗?

这是可选的,因为返回类型会被推断出来。对导出的函数和有多条 return 路径的函数,最好加上标注:标注记录了函数的约定,而且错误的 return 会在函数内部被报告,而不是在某个很远的调用处。

TypeScript 中 void 和 undefined 有什么区别?

void 的意思是“调用方不应使用返回值”。undefined 是一个具体的值类型。声明为 (): void 的函数不能返回值,但回调类型 () => void 可以接受一个确实有返回值的函数,结果只是被忽略。不返回任何值的函数用 void,只有调用方确实要把结果和 undefined 比较时才用 undefined。

如何从 TypeScript 函数返回多个值?

返回一个对象({ min: number; max: number })或元组([number, number]),然后在调用处解构。当各个值含义不同时,对象更清晰;元组适合简短的成对值,例如 [value, setValue]。

TypeScript 中 async 函数的返回类型是什么?

永远是 Promise。一个返回 number 的 async 函数,返回类型是 Promise<number>,在它上面写 : number 会产生编译错误(TS1064)。不返回任何值的函数则是 Promise<void>。

Coddy 编程语言插图

和 Coddy 一起学编程

开始