可选属性

可选属性使用 ? 标记,表示该属性可以不存在。

基本语法

interface User {
  id: number
  name: string
  email?: string
  phone?: string
}

const user1: User = { id: 1, name: "张三" }
const user2: User = { id: 2, name: "李四", email: "lisi@example.com" }
const user3: User = { id: 3, name: "王五", email: "wangwu@example.com", phone: "13800138000" }

使用场景

配置选项

interface Config {
  host: string
  port?: number
  timeout?: number
  retries?: number
}

function createClient(config: Config): void {
  const port = config.port ?? 8080
  const timeout = config.timeout ?? 5000
  const retries = config.retries ?? 3

  console.log(`连接 ${config.host}:${port}`)
}

createClient({ host: "localhost" })
createClient({ host: "localhost", port: 3000, timeout: 10000 })

表单字段

interface UserForm {
  username: string
  password: string
  email?: string
  nickname?: string
  avatar?: string
}

function register(form: UserForm): void {
  console.log(`注册用户: ${form.username}`)
}

register({ username: "zhangsan", password: "123456" })
register({ username: "lisi", password: "123456", email: "lisi@example.com", nickname: "李四" })

API 响应

interface UserResponse {
  id: number
  name: string
  email?: string
  avatar?: string
  bio?: string
}

async function getUser(id: number): Promise<UserResponse> {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}

处理可选属性

条件判断

interface User {
  name: string
  email?: string
}

function sendEmail(user: User): void {
  if (user.email) {
    console.log(`发送邮件到: ${user.email}`)
  } else {
    console.log("用户没有设置邮箱")
  }
}

默认值

interface Config {
  host: string
  port?: number
}

function connect(config: Config): void {
  const port = config.port ?? 3306
  console.log(`连接 ${config.host}:${port}`)
}

可选链

interface User {
  name: string
  address?: {
    city: string
    street: string
  }
}

function getCity(user: User): string | undefined {
  return user.address?.city
}

空值合并

interface User {
  name: string
  nickname?: string
}

function getDisplayName(user: User): string {
  return user.nickname ?? user.name
}

实际应用

部分更新

interface User {
  id: number
  name: string
  email: string
  age: number
}

type PartialUser = Partial<User>

function updateUser(id: number, data: PartialUser): void {
  console.log(`更新用户 ${id}`, data)
}

updateUser(1, { name: "新名字" })
updateUser(2, { email: "new@example.com", age: 30 })

搜索参数

interface SearchParams {
  keyword: string
  page?: number
  pageSize?: number
  sortBy?: string
  sortOrder?: "asc" | "desc"
}

function search(params: SearchParams): void {
  const page = params.page ?? 1
  const pageSize = params.pageSize ?? 10
  const sortBy = params.sortBy ?? "createdAt"
  const sortOrder = params.sortOrder ?? "desc"

  console.log(`搜索: ${params.keyword}, 第 ${page} 页`)
}

search({ keyword: "TypeScript" })
search({ keyword: "Vue", page: 2, pageSize: 20, sortBy: "name" })

注意事项

undefined 类型

可选属性的类型自动包含 undefined:

interface User {
  name: string
  email?: string
}

const user: User = { name: "张三" }

console.log(user.email)  // string | undefined

解构默认值

interface Options {
  timeout?: number
  retries?: number
}

function request(options: Options = {}): void {
  const { timeout = 5000, retries = 3 } = options
  console.log(`超时: ${timeout}ms, 重试: ${retries}次`)
}

与 null 的区别

interface User1 {
  email?: string  // string | undefined
}

interface User2 {
  email: string | null  // 必须提供,可以是 null
}

const user1: User1 = { }  // 正确
const user2: User2 = { email: null }  // 必须提供 email