在 TypeScript 中,函数参数默认是必须的,调用时必须传入对应的值。但有些情况下,某些参数可以省略,这时可以使用可选参数语法。
在参数名后添加 ? 符号,表示该参数是可选的。
function greet(name: string, greeting?: string): string {
if (greeting) {
return `${greeting}, ${name}!`
}
return `你好, ${name}!`
}
console.log(greet("张三"))
console.log(greet("李四", "早上好"))
greeting 是可选参数,调用时可以省略。在函数内部需要检查该参数是否存在。
可选参数必须放在必需参数之后。
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return `${firstName} ${lastName}`
}
return firstName
}
console.log(buildName("张"))
console.log(buildName("张", "三"))
如果可选参数在必需参数前面,编译时会报错。这是 TypeScript 的语法规则。
可选参数在未传入时,值是 undefined。可以显式传入 undefined。
function printInfo(name: string, age?: number): void {
console.log(`姓名: ${name}`)
console.log(`年龄: ${age ?? "未知"}`)
}
printInfo("张三")
printInfo("李四", 25)
printInfo("王五", undefined)
无论省略参数还是传入 undefined,函数内的行为是一致的。
可选参数的类型实际上是 T | undefined,其中 T 是声明的类型。
function process(value?: string): void {
console.log(typeof value)
if (value !== undefined) {
console.log(value.toUpperCase())
}
}
process()
process("hello")
在函数内部,需要先检查参数是否为 undefined,才能使用该参数的方法。
回调函数的参数也可以是可选的,这在定义事件处理器时很有用。
type EventHandler = (event?: Event) => void
function addListener(handler: EventHandler): void {
handler()
handler({ type: "click" } as Event)
}
addListener((event) => {
if (event) {
console.log(`事件类型: ${event.type}`)
} else {
console.log("无事件数据")
}
})
回调函数可以选择是否使用传入的事件对象。
可选参数和默认参数可以实现类似的效果,但有一些区别。
function greet1(name: string, greeting?: string): string {
return `${greeting || "你好"}, ${name}!`
}
function greet2(name: string, greeting: string = "你好"): string {
return `${greeting}, ${name}!`
}
console.log(greet1("张三"))
console.log(greet2("李四"))
console.log(greet1("王五", undefined))
console.log(greet2("赵六", undefined))
默认参数在传入 undefined 时会使用默认值,可选参数则保持 undefined。默认参数不需要放在参数列表的最后。
接口的可选属性和函数的可选参数使用相同的语法。
interface UserOptions {
name: string
age?: number
email?: string
}
function createUser(options: UserOptions): void {
console.log(`创建用户: ${options.name}`)
console.log(`年龄: ${options.age ?? "未设置"}`)
console.log(`邮箱: ${options.email ?? "未设置"}`)
}
createUser({ name: "张三" })
createUser({ name: "李四", age: 25, email: "lisi@example.com" })
接口的可选属性和函数的可选参数概念一致,都表示可以省略。
函数可以有多个可选参数,按顺序排列。
function formatAddress(
province: string,
city: string,
district?: string,
street?: string
): string {
let address = `${province} ${city}`
if (district) {
address += ` ${district}`
}
if (street) {
address += ` ${street}`
}
return address
}
console.log(formatAddress("北京市", "北京市"))
console.log(formatAddress("北京市", "北京市", "朝阳区"))
console.log(formatAddress("北京市", "北京市", "朝阳区", "建国路"))
多个可选参数时,不能跳过前面的可选参数只传后面的。如果需要跳过,可以传入 undefined。
函数重载可以更精确地描述可选参数的不同调用方式。
function createElement(tag: string): HTMLElement
function createElement(tag: string, className: string): HTMLElement
function createElement(tag: string, className?: string): HTMLElement {
const element = document.createElement(tag)
if (className) {
element.className = className
}
return element
}
const div1 = createElement("div")
const div2 = createElement("div", "container")
重载签名让函数的调用方式更加明确,IDE 也能提供更好的提示。
可选参数在实际开发中常用于配置对象和选项参数。
interface FetchOptions {
method?: "GET" | "POST" | "PUT" | "DELETE"
headers?: Record<string, string>
body?: any
}
function fetch(url: string, options?: FetchOptions): Promise<any> {
const method = options?.method || "GET"
const headers = options?.headers || {}
const body = options?.body
console.log(`请求 ${url}`)
console.log(`方法: ${method}`)
console.log(`头部:`, headers)
if (body) {
console.log(`请求体:`, body)
}
return Promise.resolve({ data: "响应数据" })
}
fetch("https://api.example.com/data")
fetch("https://api.example.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: { name: "张三" }
})
FetchOptions 的所有属性都是可选的,调用时可以只传需要的选项。
可选参数使用 ? 语法,表示调用时可以省略该参数。可选参数必须放在必需参数之后,其类型实际上是 T | undefined。在函数内部需要检查可选参数是否存在,才能安全使用。可选参数让函数接口更加灵活,适合定义有默认行为的函数。