null 和 undefined

nullundefined 是 TypeScript 中的两个基本类型,分别表示"空值"和"未定义"。

基本用法

let u: undefined = undefined
let n: null = null

console.log(u)  // undefined
console.log(n)  // null

默认行为

默认情况下,null 和 undefined 是所有类型的子类型:

let num: number = undefined  // 正确(非严格模式)
let str: string = null       // 正确(非严格模式)

严格模式

开启 strictNullChecks 后,null 和 undefined 只能赋值给自身或 any:

let num: number = undefined  // 错误
let str: string = null       // 错误

let u: undefined = undefined  // 正确
let n: null = null            // 正确

联合类型

使用联合类型处理可能为空的值:

let name: string | null = null
let age: number | undefined = undefined

name = "张三"  // 正确
age = 25      // 正确

可选属性

可选属性自动包含 undefined:

interface User {
  name: string
  age?: number
}

const user1: User = { name: "张三" }
const user2: User = { name: "李四", age: 25 }
const user3: User = { name: "王五", age: undefined }

可选参数

可选参数自动包含 undefined:

function greet(name: string, greeting?: string): string {
  return greeting ? `${greeting}${name}` : `你好,${name}`
}

greet("张三")              // "你好,张三"
greet("张三", "早上好")    // "早上好,张三"
greet("张三", undefined)  // "你好,张三"

类型守卫

检查 null

function printLength(str: string | null): void {
  if (str === null) {
    console.log("字符串为空")
    return
  }
  console.log(str.length)
}

检查 undefined

function printValue(value: string | undefined): void {
  if (value === undefined) {
    console.log("值未定义")
    return
  }
  console.log(value)
}

同时检查

function process(value: string | null | undefined): void {
  if (value == null) {  // 检查 null 和 undefined
    console.log("值为空")
    return
  }
  console.log(value)
}

非空断言

使用 ! 断言值不为 null 或 undefined:

let name: string | null = "张三"

console.log(name!.toUpperCase())  // "张三"

let element = document.getElementById("app")
element!.innerHTML = "hello"

可选链

使用 ?. 安全访问属性:

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

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

console.log(user.address?.city)  // undefined
console.log(user.address?.city?.length)  // undefined

空值合并

使用 ?? 提供默认值:

let name: string | null = null
let age: number | undefined = undefined

console.log(name ?? "匿名")     // "匿名"
console.log(age ?? 0)           // 0

// 与 || 的区别
let count = 0
console.log(count || 10)   // 10(0 是 falsy)
console.log(count ?? 10)   // 0(0 不是 null/undefined)

实际应用

API 响应处理

interface ApiResponse<T> {
  data: T | null
  error: string | null
}

function handleResponse<T>(response: ApiResponse<T>): T {
  if (response.error !== null) {
    throw new Error(response.error)
  }
  if (response.data === null) {
    throw new Error("数据为空")
  }
  return response.data
}

数据库查询

interface User {
  id: number
  name: string
  email: string | null
}

async function findUser(id: number): Promise<User | null> {
  const user = await db.query("SELECT * FROM users WHERE id = ?", [id])
  return user ?? null
}

async function getUserEmail(id: number): Promise<string> {
  const user = await findUser(id)
  if (user === null) {
    throw new Error("用户不存在")
  }
  return user.email ?? "未设置邮箱"
}

配置选项

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

function createServer(config: Config): void {
  const port = config.port ?? 3000
  const timeout = config.timeout ?? 5000

  console.log(`服务器启动: ${config.host}:${port}`)
  console.log(`超时时间: ${timeout}ms`)
}

注意事项

null vs undefined

let a: null = null
let b: undefined = undefined

console.log(a === b)  // false
console.log(a == b)   // true

typeof 检查

console.log(typeof null)       // "object"(历史遗留问题)
console.log(typeof undefined)  // "undefined"

严格模式推荐

开启严格模式可以避免很多空值问题:

{
  "compilerOptions": {
    "strictNullChecks": true
  }
}