TypeScript 基础类型:从实战角度吃透它们

说实话,很多同学学 TypeScript 都是从基础类型开始的。但我在面试中见过不少人,背了八股文,真到写代码时却用不好。今天我就结合自己的项目经验,把这些基础类型掰开揉碎了讲清楚。

布尔、数字、字符串:最常用的三兄弟

这三个类型太基础了,但我还是想多说两句。你想想看,它们虽然简单,但用不好也会出问题。

// 布尔类型
let isDone: boolean = false;
let isActive: boolean = true;

// 数字类型
let age: number = 28;
let price: number = 99.9;
let hex: number = 0xf00d;    // 十六进制
let binary: number = 0b1010; // 二进制

// 字符串类型
let name: string = "张三";
let greeting: string = `你好,${name}!`; // 模板字符串
我的小习惯:在 Vue 项目中,我习惯用模板字符串拼接路径或 URL。比如 API 地址:`/api/user/${userId}`,比用加号拼接清晰多了。

嗯,这里要注意一点。JavaScript 里没有整数和浮点数的区分,TypeScript 也一样。所有数字都是浮点数。我在项目中遇到过有人纠结要不要用 int 类型,其实完全没必要。

数组:两种写法,一种选择

数组有两种定义方式。我个人更推荐第二种,尤其是在复杂类型场景下。

// 方式一:类型 + 方括号
let list1: number[] = [1, 2, 3];

// 方式二:泛型数组
let list2: Array<number> = [1, 2, 3];

// 实际项目中的例子
let userNames: string[] = ['张三', '李四', '王五'];
let userAges: number[] = [28, 32, 25];
避坑指南:我曾经在项目中用 Array<any> 来定义数组,结果类型检查形同虚设。后来我强制团队必须指定具体类型,比如 Array<User> 或 string[]。记住,any 是万恶之源。

元组:固定长度,固定类型

元组是 TypeScript 特有的类型。说白了,就是你知道数组里每个位置是什么类型。

// 定义一个元组
let user: [string, number] = ['张三', 28];

// 访问元组元素
console.log(user[0]); // 输出: 张三
console.log(user[1]); // 输出: 28

// 实际场景:返回多个值
function getUserInfo(): [string, number] {
  return ['李四', 32];
}

我在项目中用元组最多的地方,就是处理 CSV 数据或坐标点。比如经纬度:[longitude, latitude],一眼就能看出哪个是经度哪个是纬度。

注意:TypeScript 3.0 之后,元组可以通过扩展运算符支持可变长度。但我的建议是,如果长度不确定,还是用数组吧。元组就该有元组的样子。

枚举:让代码更有语义

枚举是我在项目中用得比较多的类型。它能把数字和字符串映射成有意义的名称。

// 数字枚举
enum Direction {
  Up = 1,
  Down,
  Left,
  Right
}

// 字符串枚举
enum Status {
  Success = 'SUCCESS',
  Error = 'ERROR',
  Pending = 'PENDING'
}

// 使用枚举
let dir: Direction = Direction.Up;
let status: Status = Status.Success;

我记得有一次重构一个老项目,里面全是 0、1、2 这样的魔法数字。我改成枚举后,代码可读性直接提升了一个档次。你想想看,if (status === Status.Success)if (status === 0) 好理解多了吧?

我的建议:在 Vue 项目中,我经常用枚举来定义组件的 props 选项。比如按钮类型:type: ButtonType.Primary,比传字符串 'primary' 更安全。

any:双刃剑,慎用

any 类型是 TypeScript 的逃生舱。说白了,就是告诉编译器:别管我,我知道我在干什么。

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // 没问题,想怎么变就怎么变

// 实际场景:第三方库没有类型定义
let thirdPartyData: any = fetchSomeData();
警告:我曾经在项目里大量使用 any,结果类型检查形同虚设。后来我定了个规矩:能用 unknown 就别用 any。unknown 至少强制你做类型检查。

void、null/undefined、never:边缘但重要

这几个类型平时用得不多,但关键时刻能救命。

// void:函数没有返回值
function logMessage(message: string): void {
  console.log(message);
}

// null 和 undefined
let u: undefined = undefined;
let n: null = null;

// never:永远不会返回
function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

嗯,这里要重点说说 never。我在项目中遇到过一种情况:一个函数根据条件抛出错误或无限循环,返回值类型就是 never。它告诉 TypeScript:这个函数不会正常结束。

避坑指南:我曾经在 Vue 组件的 setup 函数里忘记处理异步错误,导致类型推断出问题。后来我习惯在 catch 块里用 never 类型来确保错误被正确处理。

类型推断:让 TypeScript 帮你干活

TypeScript 很聪明,很多时候你不用显式声明类型,它能自己推断出来。

let num = 10; // TypeScript 推断为 number
let str = "hello"; // 推断为 string
let isTrue = true; // 推断为 boolean

// 函数返回值推断
function add(a: number, b: number) {
  return a + b; // 推断返回类型为 number
}

我个人习惯是:简单类型让 TypeScript 推断,复杂类型或函数参数显式声明。这样既省事又安全。

类型断言:告诉 TypeScript 你比它懂

有时候你比 TypeScript 更了解某个值的类型。这时候可以用类型断言。

// 方式一:尖括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

// 方式二:as 语法(推荐)
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;
我的习惯:在 Vue 项目中,我统一用 as 语法。因为 JSX 里尖括号会和标签冲突,用 as 更安全。

总结一下

这些基础类型看着简单,但用好了能让你的代码质量提升一大截。我见过太多项目因为类型定义不规范,后期维护成本飙升。

记住几个原则:

  • 能用具体类型就别用 any
  • 能用枚举就别用魔法数字
  • 能用元组就别用数组(当你知道长度时)
  • 能用 unknown 就别用 any

好了,这一章就到这里。下一章我们会深入对象类型和接口,那才是 TypeScript 真正发力的地方。