第 1 节 概述
上一篇文章我们学了怎么描述"数据"的形状。但程序不光有数据,还有行为 ——而行为的基本单元就是函数。
JavaScript 的函数灵活多变:参数可多可少、返回值可有可无、同一个函数名可以有不同的调用方式。
TypeScript 给这些灵活性加上了类型约束,让函数的调用方式清晰可辨。
第 2 节 函数类型表达式
最简单的写法——给参数和返回值加类型:
1
2
3
function add ( x : number , y : number ) : number {
return x + y
}
箭头函数也一样:
1
2
3
const add = ( x : number , y : number ) : number => {
return x + y
}
类型推断可以帮你省掉一些标注:
1
2
3
4
const add = ( x : number , y : number ) => {
return x + y
}
// 返回值被推断为 number,不需要写
2.1 独立的函数类型
函数本身也有类型。可以用 type 或 interface 描述:
1
2
3
4
type BinaryFn = ( a : number , b : number ) => number
const add : BinaryFn = ( x , y ) => x + y
const multiply : BinaryFn = ( x , y ) => x * y
这在回调函数中很实用:
1
2
3
4
5
type Callback = ( error : Error | null , data : string ) => void
function fetchData ( url : string , cb : Callback ) {
// ...
}
第 3 节 可选参数与默认值
参数可以用 ? 标记为可选:
1
2
3
4
5
6
7
8
9
function greet ( name : string , greeting? : string ) {
if ( greeting ) {
return ` ${ greeting } , ${ name } `
}
return `你好, ${ name } `
}
greet ( "张三" ) // ✅ greeting 可选
greet ( "张三" , "Hello" ) // ✅
可选参数必须放在必选参数之后 :
1
function wrong ( greeting? : string , name : string ) {} // ❌ 可选参数不能在前
更常用的方式是给参数默认值 :
1
2
3
4
5
6
function greet ( name : string , greeting = "你好" ) {
return ` ${ greeting } , ${ name } `
}
greet ( "张三" ) // "你好,张三"
greet ( "张三" , "Hello" ) // "Hello,张三"
带默认值的参数自动成为可选参数,且不需要写类型注解——TypeScript 会根据默认值推断。
第 4 节 Rest 参数
当参数数量不确定时,用 ... 收集剩余参数:
1
2
3
4
5
6
function sum (... numbers : number []) : number {
return numbers . reduce (( a , b ) => a + b , 0 )
}
sum ( 1 , 2 , 3 ) // 6
sum ( 1 , 2 , 3 , 4 , 5 ) // 15
Rest 参数可以是数组或元组类型:
1
2
3
4
5
6
// 第一个参数是名字,后面是多个分数
function record ( name : string , ... scores : number []) {
console . log ( name , scores )
}
record ( "张三" , 85 , 92 , 78 )
用元组约束 Rest 参数的精确结构:
1
2
3
4
5
6
function logEvent ( event : string , ... details : [ number , string ]) {
console . log ( event , details [ 0 ], details [ 1 ])
}
// 等价于:logEvent(event: string, id: number, message: string)
logEvent ( "登录" , 1 , "用户登录成功" )
第 5 节 void 返回值
void 表示函数没有返回值,或者我们不关心它的返回值:
1
2
3
4
5
6
7
8
9
function log ( message : string ) : void {
console . log ( message )
}
function forEach < T >( items : T [], callback : ( item : T ) => void ) {
for ( const item of items ) {
callback ( item )
}
}
void 和 undefined 不同:
1
2
3
4
5
6
7
function fn1 () : void {
// 可以没有 return,也可以 return undefined
}
function fn2 () : undefined {
return undefined // 必须显式 return undefined
}
技巧
回调函数的返回值类型用 void,表示"我不在乎你返回什么"。即使回调实际返回了值,赋给 void 类型也不会报错。
第 6 节 函数重载
JavaScript 中,同一个函数名可以处理不同数量和类型的参数。TypeScript 通过重载签名 来精确描述:
1
2
3
4
5
6
7
8
9
10
11
12
// 重载签名 —— 定义所有可能的调用方式
function len ( value : string ) : number
function len ( value : unknown []) : number
// 实现签名 —— 实际逻辑
function len ( value : string | unknown []) : number {
return value . length
}
len ( "hello" ) // ✅ 匹配第一个重载
len ([ 1 , 2 , 3 ]) // ✅ 匹配第二个重载
len ( 42 ) // ❌ 没有匹配的重载
更实际的例子——根据参数类型做不同处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 重载签名
function createUser ( name : string ) : User
function createUser ( name : string , age : number ) : User
function createUser ( name : string , age : number , email : string ) : User
// 实现签名 —— 参数范围要覆盖所有重载
function createUser ( name : string , age? : number , email? : string ) : User {
return {
id : generateId (),
name ,
age : age ?? 18 ,
email : email ?? ` ${ name } @example.com` ,
}
}
// 三种调用方式都合法
createUser ( "张三" )
createUser ( "张三" , 20 )
createUser ( "张三" , 20 , "z@example.com" )
注意
重载签名只是类型声明,不包含实现。实现签名不能直接调用,它的参数类型必须能覆盖所有重载。
第 7 节 this 类型
JavaScript 函数中的 this 指向取决于调用方式。TypeScript 允许在函数的第一个参数中声明 this 的类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
interface User {
name : string
admin : boolean
}
function checkAdmin ( this : User ) {
return this . admin
}
const user = { name : "张三" , admin : true }
checkAdmin . call ( user ) // ✅ this 指向 User
checkAdmin () // ❌ this 没有绑定
在事件处理器中很实用:
1
2
3
4
5
6
7
8
9
10
11
12
interface Button {
text : string
onClick ( this : HTMLElement , event : MouseEvent ) : void
}
const button : Button = {
text : "提交" ,
onClick ( this , event ) {
// this 被限制为 HTMLElement
this . innerText = "已点击"
},
}
函数是程序的行为骨架。现在你能给参数、返回值、重载甚至 this 加类型标注了。
但有一类特殊函数还没讲——类 。类既是数据模板,也是行为容器。下一篇我们看 TypeScript 中的类。