TypeScript 引入的新类型
💡 2026 更新:本章节内容已更新至 TypeScript 5.x/6.x 标准,包含最新特性如
const type parameters、satisfies操作符等。
元组 Tuple
元组可以看做是数组的拓展,它表示已知元素数量和类型的数组。
应用场景:数组长度和类型都固定的情况下,可以使用元组进行管理!
// 基础元组
const teacherInfo: [string, string, number] = ['zws', 'jon', 18]
const teacherList: [string, string, number][] = [
['zws', 'jon', 18],
['sun', 'dea', 22],
['za2', 'wall', 26]
]
let tupleDemo: [string, number, boolean];
tupleDemo = [“a”, 2, false];
// error 不能将类型”number”分配给类型”string”。 不能将类型”string”分配给类型”number”。
tupleDemo = [2, “a”, false];
// Type '[string, number]' is not assignable to type '[string, number, boolean]'. Source has 2 element(s) but target requires 3.(2322)
tupleDemo = [“a”, 2];
let tuple: [string, number] = ['123', 123];
// 等价于
interface TupleType extends Array<number | string> {
0: string;
1: number;
length: 2;
}
let tuple1: TupleType = ['123', 123]
typescript
TypeScript 5.x+ 元组增强
可选元素和剩余元素(TS 3.0+)
// 可选元素
const tuple1: [string, number?] = ['zws'];
const tuple2: [string, number?] = ['zws', 18];
// 剩余元素
const tuple3: [string, ...number[]] = ['zws', 1, 2, 3];
const tuple4: [string, ...number[]] = ['zws']; // 剩余元素可以为空
typescript
只读元组(TS 3.4+)
const readonlyTuple: readonly [string, number] = ['zws', 18];
readonlyTuple[0] = 'jon'; // Error: Cannot assign to '0' because it is a read-only property
// 使用 Readonly 工具类型
type ReadonlyTuple = Readonly<[string, number]>;
typescript
标签元组(TS 4.0+)
// 为元组的每个元素添加标签
type Employee = [name: string, age: number, role: string];
const employee: Employee = ['Alice', 30, 'Developer'];
typescript
元组类型推断(TS 4.0+)
// TypeScript 会推断出更精确的元组类型
function tuple<T extends unknown[]>(...args: T): T {
return args;
}
const result = tuple(1, 'hello', true);
// result 的类型为 [number, string, boolean]
typescript
实战:使用元组处理函数返回值
// 元组非常适合用于返回多个值的函数
function parseInput(input: string): [success: boolean, value: number, error: string | null] {
const num = Number(input);
if (isNaN(num)) {
return [false, 0, 'Invalid number'];
}
return [true, num, null];
}
const [success, value, error] = parseInput('123');
if (success) {
console.log(`Parsed value: ${value}`);
} else {
console.error(`Error: ${error}`);
}
typescript
Enum 枚举
你可以使用常量const描述某种不会改变的值,但某些值是在一定范围内的一系列常量,如星期(周一~周日)、三原色(红黄蓝)、方位(东南西北)等,这种类型的值称为枚举。
枚举成员默认为数值类型
数值枚举
使用关键字 enum 来声明一个枚举类型数据。
enum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction.Up) // 0
console.log(Direction[0]) // "Up"
typescript
可以修改这个数值,比如你想让这个编码从 1 开始而不是 0,可以如下定义:
enum Direction {
Up = 1,
Down, // 2
Left, // 3
Right // 4
}
typescript
未赋初始值的枚举项会接着上个项的值进行递增。
也可以指定任意索引值:
enum Direction {
Up = 200,
Down = 404,
Left = 500,
Right // 默认递增
}
typescript
数字枚举在定义值的时候,可以使用计算值和常量。但是要注意,如果某个字段使用了计算值或常量,那么该字段后面紧接着的字段必须设置初始值,这里不能使用默认的递增值了,来看例子:
const getValue = () => {
return 0;
};
enum ErrorIndex {
a = getValue(),
b, // error 枚举成员必须具有初始化的值
c
}
enum RightIndex {
a = getValue(),
b = 1,
c
}
const Start = 1;
enum Index {
a = Start,
b, // error 枚举成员必须具有初始化的值
c
}
typescript
双向映射
使用 tsc 将上述代码编译为 js 后,可以发现 ts 使用 Direction[(Direction["Up"] = 0)] = "Up" 这样的内部实现对对象成员进行了双向的赋值。
由此可以看出,enum 具有双向映射的特点。
var Direction
;(function (Direction) {
Direction[(Direction['Up'] = 0)] = 'Up'
Direction[(Direction['Down'] = 1)] = 'Down'
Direction[(Direction['Left'] = 2)] = 'Left'
Direction[(Direction['Right'] = 3)] = 'Right'
})(Direction || (Direction = {}))
console.log(Direction.Up) // 0
console.log(Direction[0]) // "Up"
javascript
字符串枚举
枚举成员为字符串时,其之后的成员也必须是字符串。
enum Direction {
Up = '北',
Down = '南',
Left = '西',
Right = '东'
}
typescript
在字符串枚举中,每个成员都需要显式初始化,不能使用默认的自增行为。
常量枚举
PS: ts version 1.4 新增 const enum**(完全嵌入的枚举)**
你可以使用const和enum关键字组合,声明一个常量枚举。
特点:定义了枚举值之后,编译成 JavaScript 的代码会创建一个对应的对象,这个对象我们可以在程序运行的时候使用。而加上了const关键字后,编译后的代码不会创建这个对象(值会在编译阶段被内联):
来看我们下面的定义:
enum Status {
Off,
On
}
const enum Animal {
Dog,
Cat
}
const status = Status.On;
const animal = Animal.Dog;
typescript
上面的例子编译成 JavaScript 之后是这样的:
var Status;
(function(Status) {
Status[(Status["Off"] = 0)] = "Off";
Status[(Status["On"] = 1)] = "On";
})(Status || (Status = {}));
var status = Status.On;
var animal = 0; /* Dog */
javascript
先是定义一个变量 Status,然后定义一个立即执行函数,在函数内给 Status 添加对应属性。
- 首先
Status[“Off”] = 0是给Status对象设置Off属性,并且值设为 0,这个赋值表达式的返回值是等号右边的值,也就是 0,所以Status[Status[“Off”] = 0] = "Off"相当于Status[0] = “Off”; - 创建了这个对象之后,将 Status 的 On 属性值赋值给 status;
- 再来看下 animal 的处理,我们看到编译后的代码并没有像
Status创建一个Animal对象,而是直接把Animal.Dog的值0替换到了const animal = Animal.Dog表达式的Animal.Dog位置,这就是const enum的用法了
常量枚举中不允许使用计算值(变量或表达式)。需要注意,const enum 会在编译阶段被擦除,如果构建链路还会经过 Babel、SWC 等再次转译工具,请确认它们支持 const enum;否则可以改用普通枚举或 as const 对象字面量来获得类似的性能与类型推断。

*异构枚举
异构枚举就是枚举值中成员值既有数字类型又有字符串类型:
enum Result {
Faild = 0,
Success = "Success"
}
text
如果不是真的需要,不建议使用。因为往往我们将一类值整理为一个枚举值的时候,它们的特点是相似的。
*枚举成员类型和联合枚举类型
- 枚举成员类型
我们可以把符合条件的枚举值的成员作为类型来使用,来看例子:enum Animal { Dog = 1, Cat = 2 } interface Dog { type: Animal.Dog; // 这里使用Animal.Dog作为类型,指定接口Dog的必须有一个type字段,且类型为Animal.Dog } interface Cat { type: Animal.Cat; // 这里同上 } let cat1: Cat = { type: Animal.Dog // error [ts] 不能将类型“Animal.Dog”分配给类型“Animal.Cat” }; let dog: Dog = { type: Animal.Dog };typescript - 联合枚举类型
当我们的枚举值符合条件时,这个枚举值就可以看做是一个包含所有成员的联合类型,先来看例子:enum Status { Off, On } interface Light { status: Status; } enum Animal { Dog = 1, Cat = 2 } const light1: Light = { status: Animal.Dog // error 不能将类型“Animal.Dog”分配给类型“Status” }; const light2: Light = { status: Status.Off }; const light3: Light = { status: Status.On };typescript
上面例子定义接口 Light 的 status 字段的类型为枚举值 Status,那么此时 status 的属性值必须为 Status.Off 和 Status.On 中的一个,也就是相当于status: Status.Off | Status.On。
特殊类型
any
any:有时,我们在编写代码的时候,并不能清楚地知道一个值到底是什么类型,这时就需要用到 any 类型,即任意类型;
void
void: void 和 any 相反,any 是表示任意类型,而 void 是表示没有任意类型,就是什么类型都不是;
注意:void 类型的变量只能赋值为
undefined和null,其他类型不能赋值给 void 类型的变量。
never
never: never 类型指那些永不存在的值的类型,它是那些总会抛出异常或根本不会有返回值的函数表达式的返回值类型。
当变量被永不为真的类型保护所约束时,该变量也是 never 类型。
这三个其实跟Function有很大的关系:
// void类型:没有返回值
function sayHello(): void {
console.log('hello')
}
// never类型:函数执行不完
function errorEmitter(): never {
while (true) {}
}
// 函数的解构赋值
function add({ first, second }: { first: number; second: number }): number {
return first + second
}
add({ first: 1, second: 2 })
typescript
有的小伙伴就问了,这个never有啥用?
- 限制类型
- 控制流程
- 类型运算
举个具体点的例子,当你有一个 union type:
interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
type All = Foo | Bar
ts
在 switch 当中判断 type,TS 是可以收窄类型的 (discriminated union):
function handleValue(val: All) {
switch (val.type) {
case 'foo':
// 这里 val 被收窄为 Foo
break
case 'bar':
// val 在这里是 Bar
break
default:
// val 在这里是 never
const exhaustiveCheck: never = val
break
}
}
ts
注意在 default 里面我们把被收窄为 never 的 val 赋值给一个显式声明为 never 的变量。
如果一切逻辑正确,那么这里应该能够编译通过。
但是假如后来有一天你的同事改了 All 的类型:
type All = Foo | Bar | Baz
ts
然而他忘记了在 handleValue 里面加上针对 Baz 的处理逻辑,这个时候在 default branch 里面 val 会被收窄为 Baz,导致无法赋值给 never,产生一个编译错误。
所以通过这个办法,你可以确保 handleValue 总是穷尽 (exhaust) 了所有 All 的可能类型。
再来一个流程控制的示例:

never可以用来使得异常的处理更加安全,如果一个函数返回了never类型,那意味着这个函数不会返回给caller,这就意味着caller在调用返回never函数后的代码都成了unreachable code。
类型运算示例:
- 不相交类型的inteserction结果为never:
type result = 1 & 2 // 结果为never
typescript
- 是任何类型的subtype
type Check<T> = never extends T ? true : false
type result = check<xxx> // 结果始终为true
typescript
- 除了never,没有其他类型是never的subtype
type Check<T> = never extends never ? false : T extends never ? true : false
type result = check<xxx> // 结果始终为false
typescript
- 布尔运算
union运算的幺元,intersection运算的零元
T | never // 结果为T
T & never // 结果为never
typescript
unknown
unknown: unknown类型是TypeScript在3.0版本新增的类型,它表示未知的类型。
特点:
- 任何类型的值都可以赋值给 unknown 类型;
- 如果没有类型断言或基于控制流的类型细化时 unknown 不可以赋值给其它类型,此时它只能赋值给 unknown 和 any 类型:
let value2: unknown; let value3: string = value2; // error 不能将类型“unknown”分配给类型“string” value1 = value2;typescript - 如果没有类型断言或基于控制流的类型细化,则不能在它上面进行任何操作:
let value4: unknown; value4 += 1; // error 对象的类型为 "unknown"typescript - unknown 与任何其它类型组成的**交叉类型**,最后都等于其它类型:
type type1 = unknown & string; // type1 => string type type2 = number & unknown; // type2 => number type type3 = unknown & unknown; // type3 => unknown type type4 = unknown & string[]; // type4 => string[]typescript - unknown 与任何其它类型组成的**联合类型**,都等于 unknown 类型,但只有any例外,unknown与any组成的联合类型等于any):
type type5 = string | unknown; // type5 => unknown type type6 = any | unknown; // type6 => any type type7 = number[] | unknown; // type7 => unknowntypescript - never 类型是 unknown 的子类型:
type type8 = never extends unknown ? true : false; // type8 => truetypescript - keyof unknown 等于类型 never:
type type9 = keyof unknown; // type9 => nevertypescript - 只能对 unknown 进行等或不等操作,不能进行其它操作:
value1 === value2; value1 !== value2; value1 += value2; // errortypescript - unknown 类型的值不能访问其属性、作为函数调用和作为类创建实例:
let value5: unknown; value5.age; // error value5(); // error new value5(); // errortypescript - 使用映射类型时如果遍历的是 unknown 类型,则不会映射任何属性:
type Types<T> = { [P in keyof T]: number }; type type10 = Types<any>; // type10 => { [x: string]: number } type type11 = Types<unknown>; // type10 => {}typescript
我们在实际使用中,如果有类型无法确定的情况,要尽量避免使用 any,why?
与any的区别:
- 当一个值我们不能确定它的类型的时候,可以指定它是any类型;任意类型的值来使用,这往往会产生问题,如下:
let tmp: any console.log(tmp.name) // 当 tmp 是一个对象时,访问name属性是没问题的 console.log(tmp.toFixed()) // 当 tmp 是数值类型的时候,调用它的toFixed方法没问题 console.log(tmp.length) // 当 tmp 是字符串或数组时获取它的length属性是没问题 // 而上面的三个式子都不会报错,因为tmp是any类型typescript - 当你指定值为unknown类型的时候,如果没有通过基于控制流的类型断言来缩小范围的话,是不能对它进行任何操作;
结论:unknown相比于any是安全的
TypeScript 5.x+ 新增类型特性
const Type Parameters(TS 5.0)
TypeScript 5.0 引入了 const type parameters,允许在泛型调用时添加 const 修饰符,让编译器推断更精确的字面量类型。
// 问题:函数参数类型推断不够精确
function createOptions<T extends { mode: string; port: number }>(options: T): T {
return options;
}
// mode 被推断为 string,而不是 'development'
const options = createOptions({
mode: 'development',
port: 3000,
});
// Error: Type 'string' is not assignable to type 'development'
const mode: 'development' | 'production' = options.mode;
typescript
解决方案:使用 const 类型参数
function createOptions<const T extends { mode: string; port: number }>(options: T): T {
return options;
}
// 现在 mode 被正确推断为 'development'
const options = createOptions({
mode: 'development',
port: 3000,
});
// ✅ 正确:mode 的类型是 'development'
const mode: 'development' | 'production' = options.mode;
typescript
实际应用场景:
// 定义一个创建配置对象的函数
function defineConfig<const T>(config: T): T {
return config;
}
const config = defineConfig({
name: 'my-app',
env: 'development',
features: {
auth: true,
logging: true,
},
});
// config 的类型被精确推断为:
// {
// name: 'my-app';
// env: 'development';
// features: { auth: true; logging: true };
// }
// ❌ Error:不能修改为其他值
config.env = 'production';
// ✅ 正确:可以读取精确的字面量类型
type Env = typeof config.env; // 'development'
typescript
satisfies 操作符(TS 4.9)
satisfies 操作符允许你验证表达式是否匹配某种类型,同时保持表达式的精确类型推断。
// 问题:使用类型注解会丢失精确的类型推断
type Colors = Record<string, string | { r: number; g: number; b: number }>;
const colors: Colors = {
red: '#FF0000',
green: '#00FF00',
blue: { r: 0, g: 0, b: 255 },
};
// ❌ Error:blue 被推断为 string | { r: number; g: number; b: number }
console.log(colors.blue.r); // Error
typescript
使用 satisfies 解决:
type Colors = Record<string, string | { r: number; g: number; b: number }>;
const colors = {
red: '#FF0000',
green: '#00FF00',
blue: { r: 0, g: 0, b: 255 },
} satisfies Colors;
// ✅ 正确:blue 保留精确的类型 { r: number; g: number; b: number }
console.log(colors.blue.r); // 0
console.log(colors.red.toUpperCase()); // '#FF0000'
typescript
satisfies vs 类型注解:
| 特性 | 类型注解 (:) | satisfies |
|---|---|---|
| 类型验证 | ✅ | ✅ |
| 精确类型推断 | ❌ 丢失 | ✅ 保留 |
| 类型安全 | ✅ | ✅ |
实战案例:
// 定义样式配置
type ThemeConfig = {
colors: Record<string, string>;
spacing: Record<string, number>;
};
const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745',
},
spacing: {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
},
} satisfies ThemeConfig;
// ✅ 类型验证通过
// ✅ 保留精确的字面量类型
type PrimaryColor = typeof theme.colors.primary; // '#007bff'
type SpacingMd = typeof theme.spacing.md; // 16
// ✅ 可以使用对象方法
console.log(theme.colors.primary.toUpperCase()); // '#007BFF'
typescript
枚举的最佳实践(2026 更新)
推荐使用 const enum 或对象字面量
// ❌ 不推荐:普通枚举会生成额外代码
enum Status {
Pending = 'pending',
Success = 'success',
Error = 'error',
}
// ✅ 推荐:常量枚举(编译时内联)
const enum Status {
Pending = 'pending',
Success = 'success',
Error = 'error',
}
// ✅ 推荐:使用 as const 对象(TS 3.4+)
const Status = {
Pending: 'pending',
Success: 'success',
Error: 'error',
} as const;
// ✅ 推荐:联合类型 + 对象(最灵活)
type Status = 'pending' | 'success' | 'error';
const Status = {
Pending: 'pending',
Success: 'success',
Error: 'error',
} satisfies Record<string, Status>;
typescript
枚举 vs 联合类型对比
| 特性 | 枚举 | 联合类型 + as const |
|---|---|---|
| 类型安全 | ✅ | ✅ |
| 编译产物 | 有额外代码 | 无额外代码 |
| 反向映射 | 数字枚举支持 | 需手动实现 |
| Tree-shaking | ❌ | ✅ |
| 与第三方库交互 | 需要类型声明 | 原生支持 |
| AI 工具理解 | ⚠️ 部分工具理解困难 | ✅ 理解更好 |
2026 推荐:优先使用 联合类型 + as const 对象,性能更好且与现代工具链兼容性更佳。
// 2026 推荐写法
type Direction = 'up' | 'down' | 'left' | 'right';
const Direction = {
Up: 'up',
Down: 'down',
Left: 'left',
Right: 'right',
} satisfies Record<string, Direction>;
// 创建反向映射
const DirectionMap = Object.fromEntries(
Object.entries(Direction).map(([key, value]) => [value, key])
) as Record<Direction, keyof typeof Direction>;
// 使用
const dir: Direction = Direction.Up;
console.log(DirectionMap[dir]); // 'Up'
typescript
AI 辅助学习(2026 实战)
AI 工具推荐
当遇到复杂类型问题时,可以使用国内 AI 工具辅助:
| 工具 | 使用场景 | 示例提示词 |
|---|---|---|
| 豆包 | 类型错误诊断 | "解释这个 TS 错误:Type 'string' is not assignable to type 'never'" |
| 通义千问 | 类型推断分析 | "为什么这个变量被推断为 unknown 而不是具体类型?" |
| 智谱清言 | 最佳实践 | "枚举和联合类型哪个更好?什么时候用哪个?" |
| 腾讯元宝 | 代码重构 | "如何用 satisfies 操作符优化这段代码?" |
实战案例
案例:理解 satisfies 的使用场景
// 问题代码
const config = {
theme: 'light',
language: 'zh-CN',
};
// Error: 不能将类型"string"分配给类型"light" | "dark"
function setTheme(theme: 'light' | 'dark') {
config.theme = theme;
}
typescript
AI 辅助解决:
- 向 AI 描述问题
- AI 会建议使用
satisfies操作符 - 提供优化方案:
const config = { theme: 'light' as const, language: 'zh-CN' as const, } satisfies { theme: 'light' | 'dark'; language: string; }; // ✅ 现在 theme 的类型是 'light' setTheme(config.theme);typescript
总结
TypeScript 5.x+ 新特性要点
- const type parameters:让泛型推断更精确的字面量类型
- satisfies 操作符:类型验证 + 保留精确类型推断
- 枚举最佳实践:优先使用联合类型 + as const 对象
学习建议
- ✅ 拥抱新特性:satisfies 和 const type parameters 能显著提升类型安全性
- ✅ 避免过时用法:减少使用普通枚举,优先使用联合类型
- ✅ 善用 AI 工具:遇到复杂类型问题时,让 AI 帮你分析和解决
- ✅ 关注性能:选择编译时友好的类型定义方式(如 as const)
记住:TypeScript 的新特性是为了让代码更安全、更简洁!
↑