TypeScript 常见符号详解:用法、含义与实际应用场景

TypeScript 常见符号详解:用法、含义与实际应用场景

在学习 TypeScript 的过程中,我们经常会遇到各种各样的符号。

例如:

  • ?:表示可选属性,也可以用于可选链。

  • !:表示非空断言。

  • |:表示联合类型。

  • &:表示交叉类型。

  • =>:表示箭头函数或函数类型。

  • ...:表示展开运算符或剩余参数。

  • keyof:获取对象类型的属性名。

  • typeof:获取变量或对象的类型。

这些符号看起来很简单,但它们在 TypeScript 中承担着不同的作用。有些用于定义类型,有些用于控制程序逻辑,还有一些用于实现更复杂的类型操作。

如果只是简单记住符号的含义,很容易在实际开发中混淆。因此,本文将从基础语法、类型系统、函数、泛型和进阶类型几个方面,介绍 TypeScript 中常见符号的作用及使用场景。

一、基础语法中的常见符号

先从最常见的语法符号开始。

1. 冒号 :

冒号主要用于类型标注,告诉 TypeScript 某个变量、参数或属性应该是什么类型。

变量类型声明:

let username: string = 'Tom'
let age: number = 18
let isAdmin: boolean = false

函数参数和返回值类型:

function add(a: number, b: number): number {
  return a + b
}

其中:

  • a: number:参数 a 的类型是 number。

  • b: number:参数 b 的类型是 number。

  • ): number:函数返回值类型是 number。

对象属性类型:

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

使用场景: 变量、函数参数、函数返回值、对象属性等需要明确类型的地方。

2. 等号 =

等号主要用于赋值和默认值设置。

变量赋值:

const username = 'Tom'
let age = 18

函数默认参数:

function greet(name: string = 'Guest') {
  return `Hello, ${name}`
}

greet() // Hello, Guest
greet('Tom') // Hello, Tom

类型别名:

type ID = string | number

这里的 = 用于定义类型别名,并不是给变量赋值。

使用场景: 变量赋值、函数默认参数、类型别名定义等。

3. 逗号 ,

逗号用于分隔多个元素,常见于参数、对象和数组等场景。

const user = {
  name: 'Tom',
  age: 18,
  active: true,
}

function add(a: number, b: number) {
  return a + b
}

4. 分号 ;

分号用于结束语句。

let name: string = 'Tom';
let age: number = 18;

TypeScript 和 JavaScript 都支持自动分号插入,因此分号通常不是必需的。是否使用可以根据项目的 ESLint、Prettier 等代码规范统一决定。

5. 点号 .

点号用于访问对象属性或方法。

const user = {
  name: 'Tom',
  age: 18,
}

console.log(user.name)
console.log(user.age)

也可以用于访问模块导出的成员或对象上的方法。

6. 方括号 []

方括号在 TypeScript 中有多种用途。

定义数组类型:

const names: string[] = ['Tom', 'Jerry']
const ages: number[] = [18, 20]

定义元组:

const user: [string, number] = ['Tom', 18]

元组可以规定数组中不同位置元素的类型。

访问数组元素:

const numbers: number[] = [10, 20, 30]

console.log(numbers[0])

访问对象属性:

interface User {
  name: string
  age: number
}

const user: User = {
  name: 'Tom',
  age: 18,
}

const key: keyof User = 'name'

console.log(user[key])

7. 花括号 {}

花括号主要用于定义代码块、对象和类型结构。

定义对象:

const user = {
  name: 'Tom',
  age: 18,
}

定义接口:

interface User {
  name: string
  age: number
}

定义函数代码块:

function sayHello() {
  console.log('Hello TypeScript')
}

8. 反引号 `

反引号用于定义模板字符串,可以在字符串中插入变量或表达式。

const name = 'Tom'
const age = 18

const message = `大家好,我叫 ${name},今年 ${age} 岁。`

console.log(message)

相比普通字符串,模板字符串在拼接动态内容时更加方便。

二、TypeScript 类型系统中的常见符号

这一部分是 TypeScript 学习的重点,也是日常开发中最容易遇到的内容。

1. 联合类型 |

| 表示联合类型,代表一个值可以是多个类型中的任意一种。

let id: string | number

id = 1001
id = '1001'

上面的 id 可以是字符串,也可以是数字。

实际应用:

例如,一个用户 ID 可能来自数据库数字主键,也可能是字符串形式的 UUID:

type UserId = string | number

function getUser(id: UserId) {
  console.log(`正在查询用户:${id}`)
}

getUser(1001)
getUser('user-1001')

注意: 联合类型并不代表可以随意调用所有类型的方法。如果需要使用某一种类型特有的方法,通常需要先进行类型判断。

function printValue(value: string | number) {
  if (typeof value === 'string') {
    console.log(value.toUpperCase())
  } else {
    console.log(value.toFixed(2))
  }
}

2. 交叉类型 &

& 表示交叉类型,可以将多个类型合并为一个类型。

interface User {
  name: string
}

interface Admin {
  permissions: string[]
}

type AdminUser = User & Admin

使用时,必须同时满足两个类型的要求:

const admin: AdminUser = {
  name: 'Tom',
  permissions: ['read', 'write'],
}

实际应用:

当一个对象需要同时具备多个模块定义的属性时,可以使用交叉类型组合。

例如:

interface BaseInfo {
  id: number
  name: string
}

interface TimestampInfo {
  createdAt: Date
  updatedAt: Date
}

type User = BaseInfo & TimestampInfo

这样就可以复用不同模块定义的类型。

3. 可选属性 ?

? 用于表示属性或参数是可选的。

interface User {
  name: string
  age?: number
}

age 属性可以存在,也可以不存在。

const user1: User = {
  name: 'Tom',
}

const user2: User = {
  name: 'Jerry',
  age: 20,
}

可选函数参数:

function greet(name: string, age?: number) {
  console.log(name, age)
}

greet('Tom')
greet('Jerry', 20)

注意:可选参数通常需要放在必选参数之后。

4. 非空断言 !

! 在 TypeScript 中常用于非空断言,告诉编译器:这个值不会是 null 或 undefined。

const element = document.getElementById('app')!

element.innerHTML = 'Hello TypeScript'

这里的 ! 表示开发者确信 element 一定存在。

使用场景:

当开发者能够确定某个值一定存在,但 TypeScript 无法推断时,可以使用非空断言。

不过需要注意:

! 只会影响 TypeScript 的类型检查,不会在运行时自动检查或创建对象。如果元素实际不存在,代码仍然可能报错。

更安全的方式是进行判断:

const element = document.getElementById('app')

if (element) {
  element.innerHTML = 'Hello TypeScript'
}

5. 可选链 ?.

可选链用于安全访问可能不存在的对象属性或方法。

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

const user: User = {}

const city = user.profile?.address?.city

console.log(city)

如果访问链条中某个值是 null 或 undefined,表达式就会提前返回 undefined,不会继续访问后面的属性。

实际应用:

在处理接口返回的数据时,经常会遇到某些字段可能为空的情况:

interface ApiResponse {
  data?: {
    user?: {
      name?: string
    }
  }
}

function getUserName(response: ApiResponse) {
  return response.data?.user?.name
}

这样可以避免因为访问不存在的属性而出现运行时异常。

6. 空值合并运算符 ??

?? 用于判断左侧的值是否为 null 或 undefined。

如果左侧是 null 或 undefined,就使用右侧的默认值。

const username = undefined ?? 'Guest'

console.log(username) // Guest

它与逻辑或运算符 || 有一个重要区别:

const count1 = 0 || 10
const count2 = 0 ?? 10

console.log(count1) // 10
console.log(count2) // 0

因为:

  • || 会在左侧是假值时使用右侧值,例如 0、''、false。

  • ?? 只有在左侧为 null 或 undefined 时才使用右侧值。

使用场景: 接口字段默认值、配置项默认值、可能为空的数据处理。

7. 类型断言 as

as 用于告诉 TypeScript,开发者希望将某个值按照指定类型进行处理。

const value = 'Hello TypeScript' as string

在 DOM 操作中也很常见:

const input = document.getElementById('username') as HTMLInputElement

console.log(input.value)

注意:

类型断言不会真正改变 JavaScript 运行时的数据类型,也不会自动进行数据转换。

例如:

const value = '123' as unknown as number

这里的 value 在运行时仍然是字符串,并不会变成数字。

如果需要真正转换数据,应该使用 Number()、String() 等方法。

三、函数中的常见符号

1. 箭头符号 =>

=> 有两种常见用途:定义箭头函数和描述函数类型。

定义箭头函数:

const add = (a: number, b: number): number => {
  return a + b
}

也可以简写:

const multiply = (a: number, b: number) => a * b

定义函数类型:

type AddFunction = (a: number, b: number) => number

const add: AddFunction = (a, b) => {
  return a + b
}

这里的 => number 表示函数的返回值类型是 number。

2. 剩余参数 ...

... 可以用于收集多个参数,也可以用于展开数组或对象。

剩余参数:

function sum(...numbers: number[]): number {
  return numbers.reduce((total, current) => total + current, 0)
}

console.log(sum(1, 2, 3, 4))

数组展开:

const arr1 = [1, 2, 3]
const arr2 = [...arr1, 4, 5]

console.log(arr2)

对象展开:

const user = {
  name: 'Tom',
  age: 18,
}

const newUser = {
  ...user,
  age: 20,
}

注意:对象和数组的展开通常是浅拷贝,并不会自动复制所有嵌套对象。

3. void

void 用于表示函数没有有意义的返回值。

function logMessage(message: string): void {
  console.log(message)
}

它经常用于日志记录、事件处理等不需要返回结果的函数。

四、泛型中的常见符号和关键字

泛型是 TypeScript 非常重要的功能,可以让代码在保持类型约束的同时具备更好的复用性。

1. 泛型 <T>

<T> 通常用于声明一个泛型类型参数。

function identity<T>(value: T): T {
  return value
}

const result1 = identity<string>('Hello')
const result2 = identity<number>(100)

这里的 T 可以理解为一个类型占位符。

调用函数时,TypeScript 可以根据传入参数自动推断类型:

const name = identity('Tom')
const age = identity(18)

2. extends

extends 在 TypeScript 中有多种用途,常见的包括接口继承、泛型约束和条件类型。

接口继承:

interface Animal {
  name: string
}

interface Dog extends Animal {
  breed: string
}

泛型约束:

interface HasLength {
  length: number
}

function printLength<T extends HasLength>(value: T) {
  console.log(value.length)
}

printLength('Hello')
printLength([1, 2, 3])

这里限制了泛型 T 必须具备 length 属性。

3. keyof

keyof 用于获取某个类型的所有属性名,并将它们组成联合类型。

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

type UserKeys = keyof User

此时 UserKeys 等价于:

type UserKeys = 'name' | 'age' | 'email'

实际应用:

可以使用 keyof 限制函数参数,只允许传入对象中真实存在的属性名。

function getProperty<T, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key]
}

const user = {
  name: 'Tom',
  age: 18,
}

const name = getProperty(user, 'name')
const age = getProperty(user, 'age')

如果传入不存在的属性名,TypeScript 就会给出类型错误。

4. typeof

typeof 可以用于获取变量的类型。

const user = {
  name: 'Tom',
  age: 18,
}

type User = typeof user

此时 User 会根据 user 变量的结构推导出对应类型。

还可以结合 keyof 使用:

type UserKeys = keyof typeof user

注意:TypeScript 中的 typeof 类型查询与 JavaScript 运行时的 typeof 运算符有所区别。

JavaScript 运行时:

console.log(typeof 'Hello') // string

TypeScript 类型查询:

const config = {
  port: 3000,
}

type Config = typeof config

5. in

in 在 TypeScript 中常见于映射类型,也可以用于 JavaScript 运行时的属性判断。

映射类型:

type User = {
  name: string
  age: number
}

type ReadonlyUser = {
  readonly [K in keyof User]: User[K]
}

这里的 in 会遍历 User 的所有属性,并生成新的类型。

运行时属性判断:

const user = {
  name: 'Tom',
  age: 18,
}

console.log('name' in user) // true

两种用法需要区分:前者属于类型系统,后者属于 JavaScript 表达式。

五、接口和类型定义中的常见关键字

1. interface

interface 用于定义对象的结构和类型约束。

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

const user: User = {
  id: 1,
  name: 'Tom',
}

接口可以通过 extends 继承其他接口:

interface Admin extends User {
  permissions: string[]
}

2. type

type 用于定义类型别名,可以表示对象类型、联合类型、交叉类型、函数类型等。

type ID = string | number

type User = {
  id: ID
  name: string
}

type Status = 'success' | 'error' | 'loading'

interface 和 type 都可以描述对象结构,但它们并不完全相同。

简单来说:

  • interface 更适合定义对象结构,并支持接口继承和声明合并。

  • type 更灵活,可以定义联合类型、交叉类型、条件类型等。

3. readonly

readonly 用于限制属性只能读取,不能在初始化后重新赋值。

interface User {
  readonly id: number
  name: string
}

const user: User = {
  id: 1,
  name: 'Tom',
}

user.name = 'Jerry'

// user.id = 2 // 类型错误

需要注意,readonly 是 TypeScript 的类型检查约束,不会自动冻结运行时对象。如果需要运行时不可修改,可以考虑 Object.freeze()。

4. implements

implements 用于检查类是否满足某个接口定义。

interface Animal {
  name: string
  eat(): void
}

class Dog implements Animal {
  name = '旺财'

  eat() {
    console.log('Dog is eating')
  }
}

如果类没有实现接口要求的属性或方法,TypeScript 就会报错。

5. enum

enum 用于定义一组命名的常量。

enum Status {
  Pending = 0,
  Success = 1,
  Failed = 2,
}

const currentStatus: Status = Status.Success

在实际项目中,也可以使用字符串字面量联合类型:

type Status = 'pending' | 'success' | 'failed'

两种方式各有适用场景。简单状态标识通常可以优先考虑联合类型;需要枚举成员或相关枚举行为时,可以考虑 enum。

六、其他常见运算符

除了前面介绍的符号,还有一些 JavaScript 和 TypeScript 中经常使用的运算符。

符号

作用

示例

+

加法或字符串拼接

1 + 2

-

减法

5 - 2

*

乘法

2 * 3

/

除法

6 / 2

%

取余

5 % 2

===

严格相等

1 === 1

!==

严格不等

1 !== 2

&&

逻辑与

true && false

`

`

逻辑或

`true

false`

!

逻辑非

!true

??

空值合并

null ?? '默认值'

?.

可选链

user?.name

**

幂运算

2 ** 3

+=

加法赋值

count += 1

++

自增

count++

注意:同一个符号在不同语法上下文中可能有不同含义。例如,! 既可以表示逻辑非,也可以表示 TypeScript 非空断言;+ 既可以做数值加法,也可以做字符串拼接。

七、TypeScript 进阶类型中的常见符号

掌握基础语法之后,可以进一步了解一些更强大的类型操作。

1. 条件类型 T extends U ? X : Y

条件类型可以根据类型之间的关系选择不同的类型。

type IsString<T> = T extends string ? true : false

type A = IsString<string> // true
type B = IsString<number> // false

可以理解为类型层面的条件判断:

如果 T 可以赋值给 U,就使用 X;否则使用 Y。

2. 索引访问类型 T[K]

索引访问类型可以根据属性名获取对象中对应属性的类型。

interface User {
  name: string
  age: number
}

type UserName = User['name'] // string
type UserAge = User['age'] // number

还可以结合 keyof:

type UserValue = User[keyof User]

此时 UserValue 等价于:

type UserValue = string | number

3. as const

as const 可以让 TypeScript 尽可能保留字面量类型,并将对象属性或数组元素推断为只读类型。

const config = {
  mode: 'production',
  port: 3000,
} as const

此时:

  • mode 的类型是 'production',而不是普通的 string。

  • port 的类型是 3000,而不是普通的 number。

  • 对象属性被推断为只读。

实际应用:

const STATUS = {
  SUCCESS: 'success',
  ERROR: 'error',
  LOADING: 'loading',
} as const

type Status = typeof STATUS[keyof typeof STATUS]

得到的 Status 类型为:

type Status = 'success' | 'error' | 'loading'

4. satisfies

satisfies 用于检查一个表达式是否满足指定类型,同时尽可能保留表达式本身的类型推断。

type Config = {
  mode: 'development' | 'production'
  port: number
}

const config = {
  mode: 'production',
  port: 3000,
} satisfies Config

它和类型断言 as 不完全一样:

  • as 是告诉 TypeScript 按照指定类型处理。

  • satisfies 是检查表达式是否符合指定类型,同时保留更具体的类型信息。

5. 装饰器 @

装饰器是一种特殊语法,可以用于类及其成员的声明和元数据相关操作。

function Log(
  target: Function,
  context: ClassMethodDecoratorContext
) {
  console.log('方法装饰器:', context.name)
}

class UserService {
  @Log
  getUser() {
    return 'Tom'
  }
}

需要注意,TypeScript 装饰器存在不同的使用模型和配置要求。上面的示例采用较新的标准装饰器形式,实际项目需要确认 TypeScript 版本及所使用框架的支持情况。

八、在 Vue 3 + TypeScript 项目中的实际应用

如果你正在使用 Vue 3 + TypeScript 开发项目,上面这些符号会经常出现在组件、接口和 API 类型定义中。

下面以一个服务监控平台为例。

1. 定义服务类型

export interface Service {
  id: number
  name: string
  url: string
  status: 'online' | 'offline' | 'unknown'
  responseTime?: number
  lastCheckedAt?: string
}

这里用到了:

  • interface:定义服务对象结构。

  • ?:表示响应时间和最后检查时间可以不存在。

  • |:定义状态联合类型。

2. 定义 API 响应类型

export interface ApiResponse<T> {
  code: number
  message: string
  data: T
}

这里使用了泛型 <T>,让同一个 API 响应结构可以适用于不同的数据类型。

例如:

type ServiceListResponse = ApiResponse<Service[]>

3. 定义服务状态颜色

const statusColor = {
  online: 'green',
  offline: 'red',
  unknown: 'gray',
} as const

type ServiceStatus = keyof typeof statusColor

这里用到了:

  • as const:保留具体字面量类型。

  • typeof:获取变量的类型。

  • keyof:获取对象的属性名联合类型。

4. 在 Vue 组件中使用

<script setup lang="ts">
import { ref, computed } from 'vue'

interface Service {
  id: number
  name: string
  status: 'online' | 'offline' | 'unknown'
  responseTime?: number
}

const services = ref<Service[]>([])

const onlineCount = computed(() => {
  return services.value.filter(
    service => service.status === 'online'
  ).length
})

function updateServiceStatus(
  id: number,
  status: Service['status']
): void {
  const service = services.value.find(item => item.id === id)

  if (service) {
    service.status = status
  }
}
</script>

<template>
  <div>
    <p>在线服务数量:{{ onlineCount }}</p>

    <div v-for="service in services" :key="service.id">
      <span>{{ service.name }}</span>
      <span>{{ service.status }}</span>
    </div>
  </div>
</template>

这个例子综合使用了:

  • ref<Service[]>:定义响应式数组类型。

  • computed:计算属性。

  • =>:箭头函数。

  • Service['status']:索引访问类型。

  • ?:可选属性。

  • : void:函数返回值类型。

这些都是 Vue 3 + TypeScript 项目中非常常见的写法。

九、学习 TypeScript 符号的建议

TypeScript 的符号和关键字比较多,不建议一开始就试图全部记住。

可以按照下面的顺序学习:

优先级

知识点

需要掌握的内容

第一阶段

基础语法

:、=、[]、{}、()

第二阶段

类型系统

interface、type、?、`

、&`

第三阶段

函数与类型操作

=>、...、keyof、typeof

第四阶段

进阶类型

extends、T[K]、as const、satisfies

学习时,建议不要只记忆符号的中文含义,而是结合实际代码理解它解决了什么问题。

例如:

  • 什么时候使用 ??当一个属性不是必需的。

  • 什么时候使用 |?当一个值可能属于多种类型。

  • 什么时候使用 &?当一个对象需要同时满足多个类型。

  • 什么时候使用 keyof?当需要获取对象的属性名类型。

  • 什么时候使用 typeof?当希望从已有变量推导出类型。

十、动手练习

为了更好地理解这些符号,可以尝试完成下面几个练习。

练习一:定义用户类型

定义一个 User 类型,包含以下属性:

  • id:数字或字符串。

  • name:字符串。

  • age:可选数字。

  • email:可选字符串。

练习二:编写泛型函数

编写一个泛型函数 getFirst,接收一个数组并返回数组的第一个元素。

要求正确推断返回值类型,并处理空数组的情况。

练习三:实现服务状态管理

定义一个服务监控类型,包含服务名称、服务地址和服务状态。

服务状态只能是:

  • online

  • offline

  • unknown

再编写一个函数,根据服务 ID 修改对应服务的状态。

练习四:组合类型操作

定义一个用户基础类型和一个时间信息类型,通过交叉类型组合成完整用户类型。

然后使用 keyof 获取属性名,编写一个通用的属性读取函数。


总结

TypeScript 中的符号虽然很多,但并不需要一次性全部记住。

对于日常开发来说,最重要的是先掌握基础类型定义、联合类型、交叉类型、可选属性、函数类型和泛型。

随着项目经验增加,再逐步学习 keyof、typeof、条件类型、映射类型等进阶内容。

学习 TypeScript 最好的方式,不是背诵所有符号,而是在实际项目中不断使用它们,理解每种语法背后的设计目的。

双亲委派模型 2026-07-28