TypeScript - 数据类型详解2(TS特有类型:any、unknown、void、never、tuple)
作者:hangge | 2026-07-31 08:49
除了继承 JavaScript 的数据类型外,TypeScript 还引入了一些特有的数据类型,用于处理更复杂的类型场景。这些类型包括 any、unknown、void、never 和 tuple。本文将详细介绍这些 TypeScript 特有数据类型的使用方法、应用场景以及最佳实践,帮助大家在实际开发中选择合适的类型。










一、any 类型
1,基本使用
(1)在某些情况下,我们难以确定变量的类型,且类型可能会发生变化。这时可以使用 any 类型(类似于 Dart 语言中的 dynamic 类型)。any 类型相当于一种讨巧的 TypeScript 手段,它允许我们绕过类型检查:
// any 类型可以赋任意值
let message: any = "Hello World";
message = 123; // 可以赋值为 number
message = true; // 可以赋值为 boolean
message = {}; // 可以赋值为 object
console.log(message);

(2)any 类型也可以用于数组,表示数组可以存储任意类型的数据:
注意:虽然 any 类型很方便,但它会完全禁用类型检查,失去了 TypeScript 的类型安全优势。在实际开发中应尽量避免使用 any 类型。
// any 类型数组
const arr: any[] = [];
arr.push("hello"); // 字符串
arr.push(123); // 数字
arr.push(true); // 布尔值
console.log(arr); // ["hello", 123, true]

2,any 类型的特点
(1)any 类型具有以下特点:
- 可以对 any 类型的变量进行任何操作,包括获取不存在的属性和方法
- 可以为 any 类型的变量赋任意值
- any 类型可以赋值给任何其他类型
// any 类型的特点 let data: any = "hello"; // 可以调用任何方法(即使不存在) console.log(data.toUpperCase()); // HELLO console.log(data.foo()); // 运行时报错,但编译不报错 // any 可以赋值给任何类型 let str: string = data; // 不报错 let num: number = data; // 不报错
(2)any 类型通常用于以下场景(但应尽量缩小 any 的使用范围):
- 引入第三方库时缺少类型声明;
- 类型注解过于烦琐时临时使用;
- 从 JavaScript 迁移代码时逐步添加类型。
二、unknown 类型
1,基本使用
(1)unknown 是 TypeScript 中一种特殊的类型,用于描述类型不确定的变量。与 any 不同,unknown 是类型安全的:
提示:unknown 类型被称为安全的 any。当你不确定变量的具体类型时,应该优先使用 unknown 而非 any,这样可以强制进行类型检查后再使用。
// unknown 类型示例
function foo(): string {
return "abc";
}
function bar(): number {
return 123;
}
let flag = true;
let result: unknown; // 可接收任意类型
if (flag) {
result = foo(); // 接收 string 类型
} else {
result = bar(); // 接收 number 类型
}
console.log(result); // abc

(2)unknown 类型的一个重要特点是:它只能赋值给 any 和 unknown 类型,不能直接赋值给其他类型:
let result: unknown = "hello"; // 错误:unknown 不能赋值给 string let str: string = result; // Type 'unknown' is not assignable to type 'string'. // 错误:unknown 不能赋值给 number let num: number = result; // Type 'unknown' is not assignable to type 'number'. // 正确:unknown 可以赋值给 any let data: any = result; // 正确:unknown 可以赋值给 unknown let value: unknown = result;
2,类型收窄
(1)要使用 unknown 类型的变量,必须先进行类型检查或类型断言:
let result: unknown = "Hello TypeScript";
// 方式一:类型检查
if (typeof result === "string") {
console.log(result.toUpperCase()); // HELLO TYPESCRIPT
}
// 方式二:类型断言
console.log((result as string).length); // 16
// 方式三:类型守卫函数
function isString(value: unknown): value is string {
return typeof value === "string";
}
if (isString(result)) {
console.log(result.toLowerCase()); // hello typescript
}
(2)unknown 类型在使用前必须进行类型收窄,这是与 any 的核心区别:
| 特点 | any | unknown |
|---|---|---|
| 类型安全 | 不安全,跳过所有检查 | 安全,使用前必须检查 |
| 属性访问 | 可以访问任意属性 | 不能直接访问属性 |
| 赋值给其他类型 | 可以赋值给任何类型 | 只能赋值给 any/unknown |
| 使用场景 | 临时方案、快速迁移 | 不确定类型时的首选 |
三、void 类型
1,基本使用
(1)void 类型通常用于表示函数没有返回值:
// 没有返回值的函数,默认返回 void
function sum(num1: number, num2: number) {
console.log(num1 + num2);
}
// 显式指定返回类型为 void
function greet(name: string): void {
console.log(`Hello, ${name}!`);
}
sum(10, 20); // 30
greet("World"); // Hello, World!

(2)void 类型的函数可以返回 null 或 undefined:
注意:在严格模式(strict: true)下,void 类型函数不能返回 null,只能返回 undefined 或不返回。
// void 类型可以返回 null 或 undefined
function returnNull(): void {
return null; // 允许(需关闭 strictNullChecks)
}
function returnUndefined(): void {
return undefined; // 允许
}
function returnNothing(): void {
// 没有返回语句,隐式返回 undefined
}
2,void 与 undefined 的区别
(1)void 和 undefined 的主要区别在于语义:
// void 表示函数没有返回值(不关心返回什么)
function logMessage(msg: string): void {
console.log(msg);
}
// undefined 表示函数返回 undefined 值
function getUndefined(): undefined {
return undefined;
}
(2)在变量声明中,void 类型只能赋值为 null 或 undefined:
提示:void 类型主要用于函数返回值类型声明,很少用于变量声明。
// 声明 void 类型的变量 let unusable: void = undefined; // 在严格模式下,null 也不能赋值给 void // unusable = null; // Error if strictNullChecks is true
四、never 类型
1,基本使用
(1)never 类型表示永远不会有返回值的类型。如果一个函数陷入死循环或抛出异常,那么它的返回类型就是 never:
提示:never 类型通常用于:1)表示永远不会返回的函数;2)进行 exhaustive check(完整性检查);3)表示不可能出现的情况。
// never 类型:死循环
function infiniteLoop(): never {
while (true) {
console.log("Running...");
}
}
// never 类型:抛出异常
function throwError(message: string): never {
throw new Error(message);
}
// 调用
// infiniteLoop(); // 永远不会结束
throwError("Something went wrong"); // 抛出异常
(2)never 类型是所有类型的子类型,但没有任何类型是 never 的子类型:
// never 可以赋值给任何类型 let neverValue: never; let str: string = neverValue; // 正确 let num: number = neverValue; // 正确 let bool: boolean = neverValue; // 正确 // 但没有任何值可以赋值给 never // neverValue = "hello"; // 错误 // neverValue = 123; // 错误
2,完整性检查(Exhaustive Check)
(1)never 类型的一个重要应用是进行 switch 语句的完整性检查:
function handleMessage(message: number | string) {
switch (typeof message) {
case 'string':
console.log('String:', message);
break;
case 'number':
console.log('Number:', message);
break;
default:
// 完整性检查:如果还有未处理的类型,这里会报错
const check: never = message;
break;
}
}
handleMessage(123); // Number: 123
handleMessage("abc"); // String: abc

(2)如果修改参数类型但忘记添加对应的 case 分支,TypeScript 会报错提醒。这种机制可以帮助我们在修改代码时发现遗漏的情况,确保所有类型分支都被处理。
// 修改参数类型,增加 boolean
function handleMessage2(message: number | string | boolean) {
switch (typeof message) {
case 'string':
console.log('String:', message);
break;
case 'number':
console.log('Number:', message);
break;
default:
// 错误:boolean 类型不能赋值给 never
const check: never = message; // Type 'boolean' is not assignable to type 'never'.
break;
}
}

五、tuple 类型
1,基本使用
(1)tuple(元组)类型表示多个元素的组合,可以指定数组中每个元素的类型:
// 元组类型:指定每个元素的类型 const info: [string, number, number] = ["hangge", 88, 1.88]; // 通过索引访问,有类型提示 const name = info[0]; // 类型为 string const age = info[1]; // 类型为 number const height = info[2]; // 类型为 number console.log(name); // hangge console.log(age); // 88 console.log(height); // 1.88

(2)元组与数组的区别:
- 数组通常存放相同类型的元素,而元组可以存放不同类型的元素
- 元组中每个元素都有固定的类型,通过索引获取时有明确的类型提示
- 元组的长度是固定的,而数组长度可变
注意:元组类型虽然灵活,但元素数量是固定的。如果需要动态添加元素,应该使用数组类型。
// 数组:所有元素类型相同 const arr: any[] = ["hangge", 88, 1.88]; const name = arr[0]; // 类型为 any,没有明确提示 // 元组:每个元素类型明确 const tuple: [string, number, number] = ["hangge", 88, 1.88]; const name2 = tuple[0]; // 类型为 string,有明确提示 console.log(name2.length); // 可以调用 string 方法

2,元组的应用场景
(1)元组类型常用于函数返回多个值的场景。例如模拟 React 的 useState Hook:
// 定义返回元组:[总和: number, 平均值: number]
function calcSumAndAvg(nums: number[]): [number, number] {
const sum = nums.reduce((a, b) => a + b, 0);
const avg = sum / nums.length;
// 返回固定顺序、固定类型的元组
return [sum, avg];
}
// 解构元组接收多个返回值
const [total, average] = calcSumAndAvg([10, 20, 30]);
console.log(total); // 60
console.log(average); // 20

(2)元组也可以用于表示坐标点、键值对等固定结构的数据:
提示:在实际开发中,配合泛型使用元组可以获得更好的类型提示。例如 useState<T> 可以让返回值具有具体的类型。
// 坐标点
const point: [number, number] = [100, 200];
const [x, y] = point;
console.log(`坐标: (${x}, ${y})`); // 坐标: (100, 200)
// 键值对
const entry: [string, number] = ["score", 95];
const [key, value] = entry;
console.log(`${key}: ${value}`); // score: 95

3,可选元素与只读元组
(1)元组支持可选元素,使用 ? 标记:
// 可选元素 let tuple: [string, number?, boolean?]; tuple = ["hello"]; // 正确 tuple = ["hello", 123]; // 正确 tuple = ["hello", 123, true]; // 正确
(2)可以使用 readonly 关键字定义只读元组:
// 只读元组
const readonlyTuple: readonly [string, number] = ["hello", 123];
// 错误:不能修改只读元组
// readonlyTuple[0] = "world"; // Cannot assign to '0' because it is a read-only property.
// readonlyTuple.push("more"); // Property 'push' does not exist on type 'readonly [string, number]'.
附、TypeScript 特有类型速查表
(1)下表汇总了 TypeScript 特有数据类型的特点和使用场景:
| 类型 | 说明 | 使用场景 |
|---|---|---|
| any | 任意类型,跳过类型检查 | 临时方案、第三方库迁移 |
| unknown | 安全的任意类型 | 不确定类型时的首选 |
| void | 没有返回值 | 函数返回值类型 |
| never | 永不存在的类型 | 死循环、异常、完整性检查 |
| tuple | 固定长度和类型的数组 | 多返回值、固定结构数据 |
(2)类型选择建议:
- 优先使用具体类型,获得完整的类型提示和检查
- 不确定类型时使用 unknown,而非 any
- 函数没有返回值时使用 void
- 使用 never 进行完整性检查,确保所有分支都被处理
- 需要返回多个不同类型值时使用 tuple
全部评论(0)