Day 08 - TypeScript 基础类型

今天学完你能做什么

  • 理解 TypeScript 和 JavaScript 的关系
  • 给变量、函数参数和返回值加上类型
  • 看懂各种类型标注,不再害怕红色波浪线

一、TypeScript 是什么?——给 JavaScript 装上"安检门"

// JavaScript —— 自由但容易出错
function add(a, b) {
    return a + b;
}
add(1, "2");     // "12" —— 字符串拼接?不是你要的!
add(1, null);    // 1 —— 不报错但奇怪
add("hello", {});// "hello[object Object]" —— 什么鬼?

// TypeScript —— 加上类型约束
function add(a: number, b: number): number {
    return a + b;
}
add(1, 2);       // ✅ 3
// add(1, "2");  // ❌ 编译就报错!在你运行之前就发现了 bug

TypeScript = JavaScript + 类型系统。 它就是给 JS 装了"安检门"——不合规的数据根本进不来。


二、基础类型速览

2.1 原始类型

let name: string = "张三";         // 字符串
let age: number = 25;             // 数字(不分 int/float)
let isStudent: boolean = true;    // 布尔值
let nothing: null = null;         // 空值
let notDefined: undefined = undefined; // 未定义
let bigNumber: bigint = 100n;     // 大整数
let unique: symbol = Symbol("id");    // 唯一标识符

2.2 数组

let fruits: string[] = ["苹果", "香蕉", "橙子"];
let scores: number[] = [88, 92, 76];
let flags: boolean[] = [true, false, true];

// 泛型写法(概念在下一节讲)
let names: Array<string> = ["A", "B", "C"];

// 混合类型数组(元组——见下文)

2.3 any —— 万能类型(尽量少用)

let anything: any = "hello";
anything = 123;        // ✅ 可以
anything = true;       // ✅ 可以
anything.foo.bar();    // ✅ 不报错(但运行时会炸!)

// any 等于关闭了 TypeScript 的类型检查
// 只在迫不得已时用(比如处理第三方库的数据)

2.4 unknown —— 安全的 any

let input: unknown = "用户输入";
input = 42;
input = { data: [] };

// ❌ 不能直接使用 unknown 类型的值
// input.toUpperCase();  // 报错!

// ✅ 必须先检查类型
if (typeof input === "string") {
    console.log(input.toUpperCase());  // 安全!
}
any unknown
关掉所有检查 保留检查,必须你自己判断
不安全 安全
少用 推荐替代 any

2.5 void —— 没有返回值

// 函数不返回任何东西时用 void
function logMessage(msg: string): void {
    console.log(msg);
    // 没有 return 语句
}

function showError(msg: string): void {
    console.error(msg);
    return;  // return 后面没有值
}

2.6 never —— 永远不会返回

// 抛出异常的函数,永远不会正常返回
function throwError(msg: string): never {
    throw new Error(msg);
}

// 死循环函数
function infiniteLoop(): never {
    while (true) {
        // 永远不会结束
    }
}

三、类型推断——你不写 TS 也能猜出来

let name = "张三";        // TS 推断为 string
let age = 25;            // TS 推断为 number
let isOk = true;         // TS 推断为 boolean

// name = 123;           // ❌ 报错!TS 已经推断 name 是 string

// 数组推断
let nums = [1, 2, 3];   // number[]
let mixed = [1, "a"];   // (string | number)[]

建议: 如果 TS 能推断出来,就不用写类型。只在函数参数、复杂结构时显式标注。


四、联合类型——可能是 A 也可能是 B

// 一个变量可以是多种类型之一
let id: string | number;

id = "abc123";  // ✅
id = 456;       // ✅
// id = true;   // ❌

// 函数参数也常用
function printId(id: string | number) {
    if (typeof id === "string") {
        console.log(id.toUpperCase());
    } else {
        console.log(id.toFixed(2));
    }
}

五、字面量类型——只能是这几个值

// 限定具体的值
let direction: "left" | "right" | "up" | "down";
direction = "left";   // ✅
// direction = "forward"; // ❌

let statusCode: 200 | 404 | 500;
statusCode = 200;     // ✅
// statusCode = 300;  // ❌

// 实际应用:限制函数参数
function setAlign(align: "left" | "center" | "right") {
    console.log(`对齐方式: ${align}`);
}
setAlign("center");  // ✅
// setAlign("top");  // ❌

六、元组 Tuple——固定长度、固定类型的数组

// [string, number] 表示:第1个是 string,第2个是 number
let user: [string, number] = ["张三", 25];

// 访问
console.log(user[0].toUpperCase()); // ✅ string 方法
console.log(user[1].toFixed(2));    // ✅ number 方法

// 解构
const [userName, userAge] = user;

// 实际应用:API 返回的固定格式数据
let apiResponse: [number, string, any] = [200, "成功", { data: [] }];

七、枚举 Enum——给一组值起名字

// 数字枚举(默认从 0 开始)
enum Direction {
    Up,      // 0
    Down,    // 1
    Left,    // 2
    Right    // 3
}
let dir: Direction = Direction.Up;

// 自定义值
enum Status {
    Success = 200,
    NotFound = 404,
    ServerError = 500
}

// 字符串枚举
enum Color {
    Red = "#FF0000",
    Green = "#00FF00",
    Blue = "#0000FF"
}

八、类型别名——给类型起个外号

// 复杂类型写一次,后面复用
type UserId = string | number;
type Point = { x: number; y: number };
type Callback = (data: any) => void;

function getUser(id: UserId) { /* ... */ }
function drawPoint(p: Point) { /* ... */ }

九、可选链 ?. —— 安全地访问深层属性

9.1 没有可选链之前——层层判空,噩梦!

// 场景:获取用户的公司的地址的城市
const user = {
    name: "张三",
    company: {
        name: "某科技公司",
        address: {
            city: "北京"
        }
    }
};

// ❌ 传统写法——每层都要判空,否则报错
let city;
if (user && user.company && user.company.address) {
    city = user.company.address.city;
}
console.log(city); // "北京"

// 如果 company 不存在?
const user2 = { name: "李四" };
// user2.company.address.city  → 💥 报错!

9.2 可选链一行搞定

// ✅ 可选链 ?.  —— 前面为 null/undefined 时直接返回 undefined,不会报错
const city = user?.company?.address?.city;
console.log(city);  // "北京"

const city2 = user2?.company?.address?.city;
console.log(city2); // undefined(不报错!)

类比: 可选链就像过独木桥——每一步先伸脚试探,踩空了就退回来,不会掉下去。

9.3 可选链的各种用法

// 对象属性
const name = obj?.name;

// 数组元素
const firstItem = arr?.[0];

// 函数调用(如果函数存在才调用)
const result = callback?.();

// 组合使用
const value = data?.items?.[0]?.toUpperCase?.();

// 与空值合并 ?? 搭配
const city = user?.company?.address?.city ?? "未知城市";

十、空值合并 ?? —— 只有 null/undefined 才用默认值

10.1 ||?? 的天壤之别

// || 的坑:会把 0、""、false 都当成"假"
const count1 = 0 || 10;          // 10 —— 0 被错误地替换了!
const name1 = "" || "匿名";       // "匿名" —— 空字符串被替换了!
const enabled1 = false || true;   // true —— false 被替换了!

// ✅ ?? 只在 null 和 undefined 时才启用默认值
const count2 = 0 ?? 10;          // 0 —— 0 是有效的数字!
const name2 = "" ?? "匿名";       // "" —— 空字符串也是有效值!
const enabled2 = false ?? true;   // false 正确保留!

const count3 = null ?? 10;       // 10 —— null 才替换
const count4 = undefined ?? 10;   // 10 —— undefined 才替换

10.2 生活类比

|| 就像一个"过于热心的人":你没吃饭?我把饭给你。你只吃了一口?那也算没吃,饭拿走!
?? 就像一个"精准的人":你没吃饭(null/undefined)?给你。你吃了但没吃饱(0/"")?那是你自己的事。

10.3 实战搭配

// 可选链拿到值,空值合并给默认值——黄金搭档
function getDisplayName(user: { name?: string; nickname?: string } | null) {
    return user?.name ?? user?.nickname ?? "匿名用户";
}

console.log(getDisplayName(null));                           // "匿名用户"
console.log(getDisplayName({ name: "张三" }));                // "张三"
console.log(getDisplayName({ nickname: "小明" }));            // "小明"
console.log(getDisplayName({ name: "", nickname: "小明" }));  // ""(空字符串是有效值!)

十一、Set 与 Map —— ES6 新增数据结构

11.1 Set —— 不重复的集合

// 就像一盒巧克力,每种口味只能放一颗

// 创建
const flavors = new Set<string>();

// 添加
flavors.add("草莓");
flavors.add("抹茶");
flavors.add("草莓");  // 重复了,自动忽略
console.log(flavors);  // Set { "草莓", "抹茶" }  只有 2 个!

// 最常用:数组去重——一行搞定!
const arr = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(arr)];
console.log(unique);  // [1, 2, 3, 4]

// 其他操作
console.log(flavors.has("草莓"));  // true
console.log(flavors.size);         // 2
flavors.delete("草莓");
flavors.clear();                   // 清空

11.2 Map —— 任意类型的键值对

// 普通对象 { } 的键只能是 string 或 symbol
// Map 的键可以是任意类型:对象、数字、函数...都可以!

const cache = new Map<object, string>();

const user1 = { id: 1 };
const user2 = { id: 2 };

cache.set(user1, "张三的数据");
cache.set(user2, "李四的数据");

console.log(cache.get(user1));  // "张三的数据"
console.log(cache.size);        // 2

// 遍历
for (const [key, value] of cache) {
    console.log(key, "→", value);
}

// 判断是否存在
console.log(cache.has(user1));  // true
cache.delete(user1);

// 常见场景:用对象做键的缓存
const requestCache = new Map<string, { data: any; time: number }>();
requestCache.set("/api/users", { data: ["张三"], time: Date.now() });

11.3 Set / Map vs 普通对象/数组

需求 用这个 为什么
数组去重 new Set(arr) 天然不重复
检查元素是否存在 Set.has() O(1) 比 arr.includes() 的 O(n) 快
大量键值对增删 Map 比对象快,键可以是任意类型
固定结构的配置 普通对象 {} 简单直观

今日小结

┌───────────────────────────────────────────┐
│                                           │
│  TypeScript = JavaScript + 类型            │
│                                           │
│  基础类型:string, number, boolean         │
│  特殊类型:null, undefined, void, never    │
│  万能类型:any(少用), unknown(安全)     │
│  组合类型:联合 |, 字面量, 元组 [T, U]     │
│                                           │
│  可选链 ?.  → 安全访问,遇空返回 undefined │
│  空值合并 ?? → 只在 null/undefined 用默认  │
│  Set       → 去重集合,一行搞定            │
│  Map       → 任意类型做键的键值对          │
│                                           │
│  类型推断:不写 TS 也能猜出来               │
│  类型别名:type 关键字给类型起名字          │
│                                           │
│  口诀:冒号后面写类型,编译就检查,运行前防错 │
│                                           │
└───────────────────────────────────────────┘

更多推荐