01 - 基础类型

7 阅读4分钟

TypeScript 的核心价值在于类型系统。本章学习所有基础类型。


1.1 类型注解语法

TypeScript 使用 : 类型 的语法为变量添加类型注解:

let 变量名: 类型 = 值;

1.2 原始类型

JavaScript 中的原始类型在 TypeScript 中都有对应:

// 字符串
let name: string = "张三";

// 数字(整数和浮点数都是 number)
let age: number = 25;
let price: number = 9.99;

// 布尔值
let isDone: boolean = true;

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

// bigint(ES2020)
let big: bigint = 100n;

// symbol
let sym: symbol = Symbol("id");

⚠️ 注意:string 不等于 String。小写的 string 是 TS 类型,大写的 String 是 JS 构造函数。始终使用小写


1.3 数组

两种写法,效果完全一样:

// 写法一:类型[](推荐)
let numbers: number[] = [1, 2, 3];
let names: string[] = ["a", "b", "c"];

// 写法二:Array<类型>(泛型写法)
let numbers2: Array<number> = [1, 2, 3];

数组中的元素必须是指定的类型:

let list: number[] = [1, 2, 3];
list.push(4);      // ✅ 正确
list.push("hello"); // ❌ 错误:string 不能赋值给 number

1.4 元组(Tuple)

元组是固定长度、固定类型的数组:

// 定义一个元组:第一个是 string,第二个是 number
let person: [string, number] = ["张三", 25];

// 访问
console.log(person[0]); // "张三"
console.log(person[1]); // 25

// 错误示例
let wrong: [string, number] = [25, "张三"]; // ❌ 类型顺序不对
let wrong2: [string, number] = ["张三"];     // ❌ 缺少元素

可选元素的元组

let tuple: [string, number?] = ["hello"]; // ✅ 第二个元素可选

常见用途:函数返回多个值

function useState(initial: number): [number, (n: number) => void] {
  let value = initial;
  const setValue = (n: number) => { value = n; };
  return [value, setValue];
}

const [count, setCount] = useState(0);

1.5 枚举(Enum)

枚举用于定义一组命名常量

数字枚举

enum Direction {
  Up,    // 0
  Down,  // 1
  Left,  // 2
  Right, // 3
}

let dir: Direction = Direction.Up;
console.log(dir); // 0

// 也可以指定起始值
enum Status {
  Active = 1,
  Inactive,  // 2(自动递增)
  Deleted,   // 3
}

字符串枚举(推荐)

enum Color {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE",
}

let c: Color = Color.Red;
console.log(c); // "RED"

💡 字符串枚举更直观,调试时更容易阅读。推荐在实际项目中使用字符串枚举。

const 枚举(编译优化)

const enum Fruit {
  Apple = "APPLE",
  Banana = "BANANA",
}

let f = Fruit.Apple; // 编译后直接变成 "APPLE",没有额外对象

1.6 特殊类型

any —— 任意类型(逃生舱)

let value: any = 42;
value = "hello";  // ✅
value = true;     // ✅
value.foo.bar;    // ✅ 编译不报错,但运行时可能崩溃!

⚠️ 尽量避免使用 any,它相当于放弃了类型检查。

unknown —— 安全的 any

let value: unknown = 42;
value = "hello"; // ✅ 可以赋任何值

// 但不能直接使用,必须先缩小类型
// value.toUpperCase(); // ❌ 错误

// 正确做法:先检查类型
if (typeof value === "string") {
  console.log(value.toUpperCase()); // ✅
}

💡 需要表示"不确定的类型"时,优先用 unknown 而非 any

void —— 没有返回值

function log(msg: string): void {
  console.log(msg);
  // 没有 return,或者 return undefined
}

never —— 永远不会有值

// 函数永远不会正常结束
function throwError(msg: string): never {
  throw new Error(msg);
}

// 无限循环
function infinite(): never {
  while (true) {}
}

never 的另一个重要用途 —— 穷尽检查

type Shape = "circle" | "square" | "triangle";

function getArea(shape: Shape) {
  switch (shape) {
    case "circle":
      return Math.PI * 10 * 10;
    case "square":
      return 10 * 10;
    case "triangle":
      return (10 * 5) / 2;
    default:
      // 如果漏掉了某个 case,这里会报编译错误
      const exhaustiveCheck: never = shape;
      return exhaustiveCheck;
  }
}

1.7 类型推断

TypeScript 能自动推断类型,不需要处处写注解:

let name = "张三";      // 推断为 string
let age = 25;           // 推断为 number
let isDone = true;      // 推断为 boolean
let list = [1, 2, 3];   // 推断为 number[]

// 以下写法是多余的(类型可以被推断出来)
let name: string = "张三"; // 不需要显式写 :string

什么时候需要手动写类型注解?

// 1. 变量声明时没有赋值
let name: string;
name = "张三";

// 2. 函数参数(必须写)
function greet(name: string) {
  console.log(`Hello, ${name}`);
}

// 3. 函数返回值比较复杂时(建议写)
function parse(json: string): { name: string; age: number } {
  return JSON.parse(json);
}

1.8 类型断言

当你比 TypeScript 更了解某个值的类型时,使用类型断言:

// 写法一:as 语法(推荐)
let input = document.getElementById("myInput") as HTMLInputElement;
input.value = "hello";

// 写法二:尖括号语法(在 JSX 中不能用)
let input2 = <HTMLInputElement>document.getElementById("myInput");

非空断言 !

function getLength(str: string | undefined) {
  // 你确定 str 一定有值时
  return str!.length;
}

⚠️ 断言只是告诉编译器"相信我",不做运行时检查。用错了会导致运行时错误。


1.9 字面量类型

变量的值本身也可以作为类型:

// 字符串字面量类型
let direction: "up" | "down" | "left" | "right" = "up";
direction = "down";  // ✅
direction = "north"; // ❌ 错误

// 数字字面量类型
let dice: 1 | 2 | 3 | 4 | 5 | 6 = 3;

// 布尔字面量
let yes: true = true;

const 和字面量类型的关系

let str1 = "hello";   // 类型是 string
const str2 = "hello";  // 类型是 "hello"(字面量类型)

// 因为 const 不可变,所以 TS 推断为更精确的字面量类型

📝 练习

  1. 声明一个变量 score,类型为 number,赋值为 100
  2. 声明一个元组 user,包含用户名 (string) 和年龄 (number)
  3. 定义一个枚举 HttpStatus,包含 OK = 200, NotFound = 404, ServerError = 500
  4. 写一个函数 divide(a: number, b: number),返回两数相除的结果,b 为 0 时抛出错误
  5. unknown 类型接收一个值,通过类型缩小后调用对应方法
// 参考答案

// 1
let score: number = 100;

// 2
let user: [string, number] = ["张三", 25];

// 3
enum HttpStatus {
  OK = 200,
  NotFound = 404,
  ServerError = 500,
}

// 4
function divide(a: number, b: number): number {
  if (b === 0) throw new Error("除数不能为 0");
  return a / b;
}

// 5
function processValue(val: unknown) {
  if (typeof val === "string") {
    console.log(val.toUpperCase());
  } else if (typeof val === "number") {
    console.log(val.toFixed(2));
  }
}