《TypeScript编程入门》附录 A TypeScript 语法速查表

本附录是一份可随时翻阅的 TypeScript 语法速查表:用表格对照原始类型与字面量、数组元组枚举、函数签名、接口与类型别名、类修饰符、泛型与高级类型的写法,每张表都列出「写法 / 说明 / 常见坑」三列,帮助你在写代码时快速确认语法与易错点。

本附录目标:把全书用到的 TypeScript 语法压缩成十几张「写法 / 说明 / 常见坑」对照表。写代码时不必回忆章节号,直接翻到这里;遇到报错时,先在下表定位写法,再回正文看完整推导。

附录 A TypeScript 语法速查表

本附录不是教程,而是查阅手册。正文各章负责「为什么这样设计」,本附录只负责「怎么写、容易错在哪」。建议第一次通读一遍建立索引感,之后把它当成案头卡片。

表中的类型写法默认运行在 strict: true 之下;若你的项目未开启严格模式,部分「常见坑」不会立即报错,但问题依然存在。

原始类型与字面量类型

写法说明常见坑
let n: number = 1数字,含整数、浮点、NaN、Infinity没有 int / float 之分,金额运算注意精度
let s: string = "a"字符串,单双引号与反引号等价反引号会做插值,勿与普通串混写
let b: boolean = true布尔注解别写 Boolean(那是包装对象类型)
let big: bigint = 1n大整数,字面量带 n 后缀与 number 不能直接混合运算
let sym: symbol = Symbol()唯一符号需要 target 不低于 ES2015
let u: undefined未定义严格模式下 undefined 只等于自身
let nul: null空值严格空值检查下不兼容其他类型
let v: void函数无返回值变量类型写 void 几乎没有意义
let a: any关闭检查会沿赋值链扩散,污染下游所有类型
let un: unknown安全的顶层类型使用前必须先收窄
let nv: never永不发生的类型只应出现在抛错函数或穷尽分支
let o: object非原始值不含 null,也不等于空对象类型 {}
type T = "a" | "b"字符串字面量联合忘了 as const 时会被推断成宽泛的 string
type N = 1 | 2 | 3数字字面量联合浮点字面量同样能作为类型
type B = true | false布尔字面量等价于 boolean 的联合展开

类型注解的书写位置

位置写法说明
变量const x: number = 1变量名后加冒号
函数参数function f(a: string) {}参数名后加冒号
返回值function f(): number {}参数括号后加冒号
箭头函数const f = (a: string): number => 1同上,返回类型写在箭头前
对象属性{ id: number }属性名后加冒号
对象解构const { id }: { id: number } = o解构模式后加冒号
数组解构const [a]: [number] = arr同上,按位置对应
类字段class C { n: number = 0 }字段名后加冒号
类型断言x as number表达式后接 as
非空断言x!表达式后接叹号
满足式x satisfies T表达式后接 satisfies
类型实参f<number>(1)调用时用尖括号

数组、元组与枚举

写法说明常见坑
number[]数组简写与 Array<number> 完全等价
Array<number>泛型数组只读场景应改用 ReadonlyArray<number>
readonly number[]只读数组不能 push,但元素对象本身仍可变
[string, number]元组长度固定,越界访问会报错
[string, number?]可选元组元素可选元素只能放在末尾
[string, ...number[]]带剩余元素的元组剩余元素同样只能放末尾
readonly [string, number]只读元组常作为 as const 的推断结果出现
enum Color { Red, Green }数字枚举默认从 0 开始,且运行时存在反向映射
enum Color { Red = "red" }字符串枚举无反向映射,对外契约优先选它
const enum Dir { Up }常量枚举与 isolatedModules 冲突,跨包慎用
as const字面量冻结对象变只读,数组变只读元组

as const 的效果值得单独对照:

const a = ["x", "y"];
// string[]

const b = ["x", "y"] as const;
// readonly ["x", "y"]

const c = { kind: "user" };
// { kind: string }

const d = { kind: "user" } as const;
// { readonly kind: "user" }

函数签名

写法说明常见坑
function f(a: string): void普通函数返回类型可省略,但公开 API 建议显式写出
(a: string) => void函数类型参数名可省略,只留类型
function f(a?: string)可选参数可选参数必须排在必填参数之后
function f(a = 1)默认值有默认值即隐式为可选,类型可自动推断
function f(...rest: number[])剩余参数只能是最后一个参数
function f(this: Window)this 参数只在类型层存在,不产生真实形参
重载签名 + 实现签名重载实现签名对外不可见,且必须写在最后
function f(a: string | number)联合参数实现签名必须兼容全部重载签名
(...args: any[]) => any任意函数赋给 Function 会丢失全部参数类型
f?: () => void可选回调调用前需判空,或用 f?.()
type F = { (a: string): void; tag: string }可调用对象用 interface 写可读性更好

重载与实现签名的关系:

function parse(v: string): number;
function parse(v: number): string;
function parse(v: string | number): string | number {
  return typeof v === "string" ? Number(v) : String(v);
}

const n = parse("1"); // number
const s = parse(1);   // string

接口与类型别名

写法说明常见坑
interface User { id: number }定义对象结构同名 interface 会自动声明合并
type User = { id: number }类型别名同名 type 重复声明会直接报错
{ id?: number }可选属性与「值为 undefined」不等价,见 exactOptionalPropertyTypes
{ readonly id: number }只读属性只防重新赋值,不防内层可变
{ [k: string]: number }索引签名会要求所有具名属性都兼容该签名
{ [k: string]: number; id: number }具名 + 索引具名属性类型必须是索引类型的子类型
interface A extends B {}接口继承可多继承,冲突成员类型不兼容会报错
type A = B & C交叉类型同名属性类型冲突时会被推成 never
interface A { m(): void }方法简写与方法属性写法在严格函数类型下有细微差别
type Union = A | B联合类型访问成员前必须先收窄
type Fn = (a: string) => void函数类型别名interface 也能表达,但简写更常见

类

写法说明常见坑
class C {}类类同时拥有「实例类型」与「构造器类型」
public / protected / private访问修饰符private 只在类型层,运行时仍可访问
#field真私有字段运行时真私有,需要 target 不低于 ES2022
readonly id: number只读字段只能在声明处或构造器内赋值
constructor(private name: string) {}参数属性自动生成同名字段,可与修饰符、readonly 组合
static count = 0静态成员静态成员属于构造器类型,不属于实例
abstract class A {}抽象类不能 new,抽象方法必须在子类实现
class C implements I {}接口实现只做形状检查,不产生任何继承关系
class C extends B {}继承子类构造器必须先 super() 再用 this
override m() {}显式覆写需要 noImplicitOverride 配合才强制
get x() {} / set x(v) {}访问器较新版本可分别声明读写类型,旧版必须一致
declare field: T仅声明不产出与 useDefineForClassFields 的语义相关

泛型

写法说明常见坑
<T>(x: T) => T泛型函数参数名随意,约定用 T / U / K / V
<T extends object>泛型约束不加约束时不能访问任何成员
<T extends keyof U>键约束常与索引访问 U[T] 配合
<T = string>默认类型参数只有无法推断时才会生效
<const T>const 类型参数让实参按字面量推断,免写 as const
<T,>(x: T) => x箭头函数中的泛型.tsx 文件里必须加逗号,否则被当成 JSX
class Box<T> {}泛型类静态成员不能引用类的类型参数
interface Repo<T> {}泛型接口方法级还能再引入独立参数
Map<K, V>多参数泛型参数顺序即语义,不要随意调换
T extends U ? X : Y泛型上的条件会分发联合类型,用 [T] extends [U] 关闭分发

高级类型

写法说明常见坑
keyof T键的联合对联合类型取 keyof 得到的是公共键
typeof x取变量的类型只对值有效,不能作用于类型别名
T[K]索引访问类型K 必须是 keyof T 的子集
{ [K in keyof T]: T[K] }映射类型默认保留修饰符,用 -? / -readonly 移除
{ [K in keyof T as NewKey]: T[K] }键重映射重映射成 never 即可删除该键
T extends U ? X : Y条件类型裸 T 会分发联合,[T] 包裹可关闭
T extends (infer R)[] ? R : neverinfer 推断同一条件里同名 infer 会互相约束
infer R extends string受限 infer旧版本不支持,需改用嵌套条件
`get${Capitalize<K>}`模板字面量类型只能作用于字符串、数字、布尔字面量
Uppercase<S>字符串工具类型仅内置四个,其他大小写规则需自写
T & { __brand: "x" }品牌类型运行时不存在,仅在类型层区分
Awaited<T>递归解开 Promise手写 infer 版本容易漏掉多层嵌套

模块与声明

写法说明常见坑
import { a } from "./m"具名导入路径解析受 moduleResolution 影响
import type { T } from "./m"仅类型导入编译后整句消失,不产生运行时副作用
export type { T }仅类型导出与 isolatedModules 配合时必需
export default f默认导出与 CJS 互操作时命名容易错位
export = fCJS 风格导出不能与 ESM 语法混用
import x = require("m")CJS 风格导入仅 CJS 输出目标可用
declare const VERSION: string声明值只描述已存在的运行时值,不产出代码
declare module "m" {}声明模块为无类型库打补丁的标准做法
declare global {}全局增强所在文件必须是模块(含 import 或 export)
declare namespace N {}命名空间新代码优先用模块,命名空间主要用于声明
/// <reference types="node" />三斜线指令现代项目多用 tsconfig 的 types 字段替代

类型断言与收窄

写法说明常见坑
x as T类型断言只在兼容方向允许,跨类型需先 as unknown
x!非空断言断言错了运行时照样崩,慎用
x satisfies T满足式检查兼容性但不改变推断结果
typeof x === "string"typeof 守卫只对原始类型精确
x instanceof Dateinstanceof 守卫跨 iframe 或多 realm 场景会失效
"a" in xin 守卫只收窄到含该键的成员
Array.isArray(x)内置守卫对只读数组需额外处理
x is T自定义类型守卫返回布尔,但正确性由你保证
asserts x is T断言函数函数返回 void,调用后立即收窄
asserts x断言真值与上者区别:只断言非空,不改变类型
switch + never 兜底穷尽检查需要开启严格空值检查才有效
const _x: never = value穷尽兜底漏掉分支时恰好在此处报错

类型推断与显式标注的取舍

「该不该写类型」是新手最常纠结的问题。判断标准只有一条:这行代码的类型是否可能与你预期不同。

场景建议理由
const n = 1不写推断为字面量 1,比手写更精确
let n = 1不写推断为 number,符合预期
const obj = { a: 1 }不写结构清晰,推断即所得
函数返回值(内部)不写让实现即契约,减少重复
函数返回值(导出)必须写公开 API 的契约必须显式
空数组 []必须写否则推断为空联合,push 报 never
空对象 {}必须写否则无法索引任何属性
回调参数不写由被调用方上下文推断
泛型函数调用尽量不写写死类型实参会丢掉推断的精确性
第三方数据边界必须写类型是「声称」,需运行时校验兜底

一个反例说明「不写反而更好」:

// 写死了宽类型,丢掉了字面量信息
const config: Record<string, string> = { mode: "dark" };
// config.mode 的类型是 string,不是 "dark"

// 让编译器推断,保留字面量
const config2 = { mode: "dark" } as const;
// config2.mode 的类型是 "dark"

常见坑速查

症状原因解决
字面量被推断成 string缺少 as const 或 const 类型参数加 as const,或用 <const T>
可选属性传 undefined 报错开启了 exactOptionalPropertyTypes显式写 id?: number | undefined
Object is possibly 'undefined'严格空值检查生效判空、可选链 ?.、或默认值
联合类型上调方法报错方法不在所有成员上先用守卫收窄,或用判别联合
重载调用总是匹配不到实现签名与重载签名不兼容让实现签名参数放宽为各重载的并集
交叉类型属性变成 never两类型同名属性不兼容改用 Omit 去掉冲突字段再交叉
索引签名报「属性不兼容」具名属性类型不是索引类型的子类型让索引类型覆盖全部具名属性类型
as 之后运行时报错断言只是让编译器闭嘴用类型守卫或运行时校验替代
枚举值意外相等数字枚举成员自动递增对外契约改用字符串枚举

小结

  • 本附录按「类型 → 注解位置 → 数组元组 → 函数 → 接口 → 类 → 泛型 → 高级类型 → 模块 → 收窄」的顺序组织,每一节都是一张可直接查的表。
  • 表中的「常见坑」列才是真正值钱的部分:绝大多数 TypeScript 报错不是语法不会写,而是推断结果与预期不同。
  • 类型注解的书写位置只有「冒号、as、satisfies、尖括号」四种形态,记熟这四种就不会找不到该在哪里写类型。
  • 高级类型全部建立在 keyof、索引访问、映射类型、条件类型四件事之上,遇到复杂类型先拆成这四步。
  • 遇到报错时,先在「常见坑速查」表里定位症状,再回到对应正文小节看完整推导。
  • 附录 B 会在这份语法表之上,继续整理标准库工具类型与常用类型模式。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「typescript」更多文章

  1. 《TypeScript高级编程》11.3 类型驱动架构与团队规范
  2. 《TypeScript高级编程》11.2 渐进式迁移与严格化路径
  3. 《TypeScript高级编程》11.1 TS 版本演进与 breaking changes