function-type-expressions
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseApply Types to Entire Function Expressions When Possible
尽可能为整个函数表达式应用类型
Overview
概述
Type entire functions at once instead of individual parameters.
When using function expressions (not statements), you can apply a type to the entire function. This reduces repetition, improves type safety, and makes code more readable.
一次性为整个函数指定类型,而非为单个参数分别指定。
当使用函数表达式(而非函数声明)时,你可以为整个函数应用一个类型。这减少了重复代码,提升了类型安全性,还能让代码更具可读性。
When to Use This Skill
适用场景
- Writing multiple functions with the same signature
- Implementing callbacks for libraries
- Matching signatures of existing functions
- Wrapping or extending existing functions
- 编写多个具有相同签名的函数
- 为类库实现回调函数
- 匹配现有函数的签名
- 包装或扩展现有函数
The Iron Rule
铁则
When functions share a signature, define the type ONCE and apply it to EACH function.Remember:
- Function expressions can have types applied to them
- Parameter types are inferred from the function type
- Return types are checked against the function type
- Use to match existing function signatures
typeof fn
当多个函数共享同一签名时,只需定义一次类型,然后将其应用到每个函数上。注意:
- 函数表达式可以应用类型
- 参数类型会从函数类型中自动推断
- 返回类型会与函数类型进行校验
- 使用 来匹配现有函数的签名
typeof fn
Detection: Repeated Signatures
识别重复签名
typescript
// Repetitive - same signature 4 times
function add(a: number, b: number) { return a + b; }
function sub(a: number, b: number) { return a - b; }
function mul(a: number, b: number) { return a * b; }
function div(a: number, b: number) { return a / b; }typescript
// 重复冗余 - 4次使用相同签名
function add(a: number, b: number) { return a + b; }
function sub(a: number, b: number) { return a - b; }
function mul(a: number, b: number) { return a * b; }
function div(a: number, b: number) { return a / b; }The Solution: Function Types
解决方案:函数类型
Define Once, Use Many Times
一次定义,多次使用
typescript
type BinaryFn = (a: number, b: number) => number;
const add: BinaryFn = (a, b) => a + b; // Types inferred
const sub: BinaryFn = (a, b) => a - b;
const mul: BinaryFn = (a, b) => a * b;
const div: BinaryFn = (a, b) => a / b;Benefits:
- No repeated type annotations
- Return type checked automatically
- Logic is more visible without type noise
typescript
type BinaryFn = (a: number, b: number) => number;
const add: BinaryFn = (a, b) => a + b; // 类型自动推断
const sub: BinaryFn = (a, b) => a - b;
const mul: BinaryFn = (a, b) => a * b;
const div: BinaryFn = (a, b) => a / b;优势:
- 无需重复编写类型注解
- 返回类型会自动校验
- 去掉类型干扰后,逻辑更清晰
Match Existing Functions with typeof
使用typeof匹配现有函数
typescript
// Match fetch's signature exactly
const checkedFetch: typeof fetch = async (input, init) => {
const response = await fetch(input, init);
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response;
};TypeScript infers:
input: RequestInfo | URLinit?: RequestInit- Return:
Promise<Response>
typescript
// 完全匹配fetch的签名
const checkedFetch: typeof fetch = async (input, init) => {
const response = await fetch(input, init);
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response;
};TypeScript会自动推断:
input: RequestInfo | URLinit?: RequestInit- 返回值:
Promise<Response>
Change Return Type with Parameters
保留参数类型,修改返回类型
typescript
// Match parameters but change return type
async function fetchNumber(
...args: Parameters<typeof fetch>
): Promise<number> {
const response = await checkedFetch(...args);
return Number(await response.text());
}typescript
// 匹配参数类型,但修改返回类型
async function fetchNumber(
...args: Parameters<typeof fetch>
): Promise<number> {
const response = await checkedFetch(...args);
return Number(await response.text());
}Function Statement vs Expression
函数声明 vs 函数表达式
typescript
// Statement - must annotate each parameter
function rollDice1(sides: number): number { /* ... */ }
// Expression - can apply type to entire function
type DiceRollFn = (sides: number) => number;
const rollDice2: DiceRollFn = (sides) => { /* ... */ };typescript
// 函数声明 - 必须为每个参数添加注解
function rollDice1(sides: number): number { /* ... */ }
// 函数表达式 - 可为整个函数应用类型
type DiceRollFn = (sides: number) => number;
const rollDice2: DiceRollFn = (sides) => { /* ... */ };Common Function Type Patterns
常见函数类型模式
Callback Types
回调类型
typescript
type EventHandler = (event: Event) => void;
type AsyncCallback<T> = () => Promise<T>;
type Comparator<T> = (a: T, b: T) => number;
const handleClick: EventHandler = (e) => {
console.log(e.target); // e is typed as Event
};typescript
type EventHandler = (event: Event) => void;
type AsyncCallback<T> = () => Promise<T>;
type Comparator<T> = (a: T, b: T) => number;
const handleClick: EventHandler = (e) => {
console.log(e.target); // e被自动推断为Event类型
};Generic Function Types
泛型函数类型
typescript
type Mapper<T, U> = (item: T, index: number) => U;
const double: Mapper<number, number> = (n) => n * 2;
const stringify: Mapper<number, string> = (n) => String(n);typescript
type Mapper<T, U> = (item: T, index: number) => U;
const double: Mapper<number, number> = (n) => n * 2;
const stringify: Mapper<number, string> = (n) => String(n);Interface Syntax (Alternative)
接口语法(替代方案)
typescript
// Function type as interface
interface StringTransform {
(input: string): string;
}
const toUpper: StringTransform = s => s.toUpperCase();typescript
// 用接口定义函数类型
interface StringTransform {
(input: string): string;
}
const toUpper: StringTransform = s => s.toUpperCase();Return Type Safety
返回类型安全性
Function types catch return type errors:
typescript
const checkedFetch: typeof fetch = async (input, init) => {
const response = await fetch(input, init);
if (!response.ok) {
return new Error('Failed'); // Error!
// Type 'Error' is not assignable to type 'Response'
}
return response;
};Without the function type, this would only error at call sites.
函数类型可以捕获返回类型错误:
typescript
const checkedFetch: typeof fetch = async (input, init) => {
const response = await fetch(input, init);
if (!response.ok) {
return new Error('Failed'); // 错误!
// 类型'Error'无法赋值给类型'Response'
}
return response;
};如果没有使用函数类型,这个错误只会在调用该函数时才会被发现。
Library Callback Types
类库回调类型
Libraries often provide callback types:
typescript
// React provides these
import { MouseEventHandler, ChangeEventHandler } from 'react';
const handleClick: MouseEventHandler<HTMLButtonElement> = (e) => {
console.log(e.currentTarget.disabled); // Fully typed
};
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
console.log(e.target.value); // Fully typed
};类库通常会提供预定义的回调类型:
typescript
// React提供了这些类型
import { MouseEventHandler, ChangeEventHandler } from 'react';
const handleClick: MouseEventHandler<HTMLButtonElement> = (e) => {
console.log(e.currentTarget.disabled); // 类型完全推断
};
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
console.log(e.target.value); // 类型完全推断
};When NOT to Use Function Types
不适用场景
Don't over-engineer for single functions:
typescript
// Overkill for a single standalone function
type GreetFn = (name: string) => string;
const greet: GreetFn = (name) => `Hello, ${name}`;
// Just use a normal function statement
function greet(name: string): string {
return `Hello, ${name}`;
}不要为单个函数过度设计:
typescript
// 单个独立函数用这个太冗余
type GreetFn = (name: string) => string;
const greet: GreetFn = (name) => `Hello, ${name}`;
// 直接使用普通函数声明即可
function greet(name: string): string {
return `Hello, ${name}`;
}Pressure Resistance Protocol
应对质疑的方案
1. "Function Statements Are Clearer"
1. "函数声明更清晰"
Pressure: "I prefer seeing types inline with parameters"
Response: With shared signatures, centralizing the type removes noise.
Action: Use function types when 2+ functions share a signature.
质疑: "我更喜欢在参数旁直接看到类型"
回应: 当多个函数共享签名时,集中定义类型可以减少冗余干扰。
行动: 当有2个及以上函数共享签名时,使用函数类型。
2. "I Don't Know the Library's Type"
2. "我不知道类库的类型"
Pressure: "I can't find the callback type in the library"
Response: Use or .
typeof existingFunctionParameters<typeof fn>Action: Match existing signatures with typeof.
质疑: "我找不到类库中的回调类型"
回应: 使用 或 来匹配。
typeof existingFunctionParameters<typeof fn>行动: 用typeof匹配现有函数的签名。
Red Flags - STOP and Reconsider
危险信号 - 立即反思
- Same parameter types repeated across multiple functions
- Wrapper functions that should match the wrapped function's signature
- Callbacks without proper typing
- 多个函数重复使用相同的参数类型
- 包装函数未匹配原函数的签名
- 回调函数没有正确的类型
Common Rationalizations (All Invalid)
常见借口(均不成立)
| Excuse | Reality |
|---|---|
| "Types on parameters are clearer" | Not when repeated 4 times |
| "Function statements are simpler" | Function expressions with types are just as clear |
"I'll just use | Loses all type safety benefits |
| 借口 | 实际情况 |
|---|---|
| "参数上的类型更清晰" | 当重复4次时就不是了 |
| "函数声明更简单" | 带类型的函数表达式同样清晰 |
| "我直接用any就行" | 完全丧失了类型安全的优势 |
Quick Reference
快速参考
typescript
// Define function type
type Transform<T> = (value: T) => T;
// Apply to function expression
const double: Transform<number> = v => v * 2;
// Match existing function
const myFetch: typeof fetch = async (input, init) => { ... };
// Match parameters, change return
function wrapper(...args: Parameters<typeof original>): NewReturn { ... }typescript
// 定义函数类型
type Transform<T> = (value: T) => T;
// 应用到函数表达式
const double: Transform<number> = v => v * 2;
// 匹配现有函数
const myFetch: typeof fetch = async (input, init) => { ... };
// 匹配参数,修改返回类型
function wrapper(...args: Parameters<typeof original>): NewReturn { ... }The Bottom Line
总结
Apply types to entire function expressions when you have shared signatures.
This reduces repetition, centralizes type definitions, and catches return type errors at the source. Use to match existing function signatures exactly.
typeof当函数共享签名时,为整个函数表达式应用类型。
这减少了重复代码,集中管理类型定义,并能在源头捕获返回类型错误。使用来完全匹配现有函数的签名。
typeofReference
参考资料
Based on "Effective TypeScript" by Dan Vanderkam, Item 12: Apply Types to Entire Function Expressions When Possible.
基于Dan Vanderkam所著《Effective TypeScript》第12条:尽可能为整个函数表达式应用类型。