在现代前端全栈开发中,Docker 已经成为本地搭建服务环境的标配,而 TypeScript 高级类型则是提升代码健壮性的关键武器。很多同学在日常使用中经常对 docker run/exec 混淆不清,对 Pick/Omit/Exclude 等工具类型一知半解。本文就以最常用的 MySQL 容器部署和真实业务场景为例,一次性把这两套工具的核心用法、底层逻辑和易混点讲透。
一、Docker 核心命令实战:以 MySQL 部署为例
很多人接触 Docker 都是从拉镜像、跑容器开始,但 run 和 exec 的区别、参数的含义常常搞混。我们以启动一个 MySQL 8.0 容器为例,逐层拆解。
1. 启动容器:docker run 全参数拆解
我们先看一条完整的后台启动 MySQL 容器的命令:
css
docker run -d --name mysql-demo -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
这条命令看起来参数多,其实每一个都有明确的职责,我们逐个拆解:
表格
| 参数 | 作用 | 注意事项 |
|---|---|---|
docker run |
创建并启动一个全新的容器 | 每次 run 都是生成一个新实例 |
-d |
后台守护进程模式运行 | 不加 -d 会前台占用终端,Ctrl+C 容器就停止 |
--name mysql-demo |
给容器指定名称 | 后续 stop/rm/exec 都可以用这个名字操作 |
-p 3307:3306 |
端口映射:宿主机端口:容器内部端口 | 顺序不能写反;容器内 MySQL 默认端口 3306 不要改 |
-e MYSQL_ROOT_PASSWORD=123456 |
注入环境变量,设置 MySQL root 密码 | 变量名是 MySQL 镜像规定的,拼写不能错 |
mysql:8.0 |
使用的镜像及版本 | 镜像名必须写在命令最后 |
✅ 划重点:参数的顺序可以互换,但镜像名必须在整条命令的末尾。
2. 进入容器:docker exec -it 到底是什么
容器启动后,我们经常需要进到容器内部操作,比如登录 MySQL。这时候用的就是 docker exec:
bash
docker exec -it mysql-demo /bin/bash
很多人只会复制粘贴,却不知道 -it 到底是什么意思,这里一次性讲透:
exec:在已经运行中的容器里执行一条命令,不会新建容器-i(--interactive):保持标准输入打开,让容器能接收你的键盘输入-t(--tty):分配一个伪终端,才会出现我们熟悉的命令行提示符
⚠️ 前提条件:容器必须处于
Up运行状态,停止的容器无法 exec。
执行完这条命令,你就相当于 "远程登录" 进了这台迷你 Linux 机器,可以执行各种 Linux 命令。接下来就可以在容器内登录 MySQL:
mysql -uroot -p123456
3. 一句话分清 run 和 exec
这是 Docker 入门最容易混淆的点,记住这个类比就不会忘:
docker run= 买一台新电脑,开机装系统并启动docker exec= 电脑已经开着,你远程桌面连接进去操作
4. 完整实战流程
bash
# 1. 后台启动 MySQL 容器
docker run -d --name mysql-demo -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
# 2. 查看容器是否运行成功
docker ps
# 3. 进入容器内部 bash 终端
docker exec -it mysql-demo /bin/bash
# 4. 在容器内登录 MySQL
mysql -uroot -p123456
# 5. 退出 MySQL → 退出容器 → 停止并删除容器
exit
exit
docker stop mysql-demo
docker rm mysql-demo
🚩 踩坑提醒:当前命令没有挂载数据卷,
docker rm后数据库数据会全部丢失,仅适合测试练习。
二、TypeScript 7 大高级工具类型深度解析
TypeScript 内置了很多工具类型,能帮我们快速复用和改造类型,避免重复写代码。下面这 7 个是日常开发中最高频的,我们逐个讲透用法、场景和底层逻辑。
前置基础:keyof 操作符
在讲工具类型之前,必须先理解 keyof,它是所有对象工具类型的基石。 keyof T 可以取出一个对象类型的所有属性名,组成一个联合类型。
ini
interface User {
id: number;
name: string;
email: string;
}
type UserKeys = keyof User;
// 等价于:type UserKeys = 'id' | 'name' | 'email'
简单说:把对象的键,变成字符串联合类型。
1. Pick<T, K>:挑选指定属性
作用:从类型 T 中,挑选出指定的 K 属性,生成一个新的对象类型。
ini
type UserPreview = Pick<User, 'id' | 'name'>;
// 等价于:
// type UserPreview = {
// id: number;
// name: string;
// }
内置实现:
scala
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
使用场景:列表页预览对象,只需要 id 和 name,不需要完整的用户信息。
2. Omit<T, K>:剔除指定属性
作用:从类型 T 中,剔除掉指定的 K 属性,剩下的属性组成新类型。和 Pick 刚好相反。
ini
type UserWithoutEmail = Omit<User, 'email'>;
// 等价于:
// type UserWithoutEmail = {
// id: number;
// name: string;
// }
重点:Omit 的底层等价实现
这是面试高频考点:Omit<T, K> 等价于 Pick<T, Exclude<keyof T, K>>。
我们分步拆解这个逻辑,你就彻底懂了:
- 第一步:
keyof T拿到 T 所有属性名的联合类型:'id' | 'name' | 'email' - 第二步:
Exclude<联合类型, K>从联合里删掉要排除的'email',得到'id' | 'name' - 第三步:
Pick<T, 剩下的属性>从原对象里挑出剩下的属性
整个流程就是:对象 → 取出所有键 → 过滤掉不要的键 → 用剩下的键重新挑选对象。 TS 内部的 Omit,本质就是这三步的语法糖。
3. Partial<T>:全部属性变可选
作用:把类型 T 的所有属性全部变成可选属性。
ini
type PartialUser = Partial<User>;
// 等价于:
// type PartialUser = {
// id?: number;
// name?: string;
// email?: string;
// }
内置实现:
ini
type Partial<T> = {
[P in keyof T]?: T[P];
};
使用场景:编辑接口的入参,只需要传要修改的字段,不需要传完整对象。
php
function updateUser(id: number, data: Partial<User>) {}
updateUser(1, { name: '张三' }); // ✅ 合法
4. Exclude<T, U>:联合类型排除
作用:从联合类型 T 中,排除掉可以赋值给 U 的部分,返回剩下的联合类型。
ini
type All = 'id' | 'name' | 'email';
type AfterExclude = Exclude<All, 'email'>;
// 结果:'id' | 'name'
✅ 划重点:Exclude 操作的是联合类型,不是对象类型;操作对象类型用 Omit。
内置实现:
r
type Exclude<T, U> = T extends U ? never : T;
5. ReturnType<T>:获取函数返回值类型
作用:提取一个函数类型的返回值类型。
bash
function getUser() {
return { id: 1, name: '张三' };
}
type UserResult = ReturnType<typeof getUser>;
// 结果:{ id: number; name: string; }
注意:参数必须是函数类型 ,所以要先用
typeof fn拿到函数的类型,不能直接写ReturnType<fn>。
6. Record<K, V>:快速定义键值字典
作用:快速定义一个键类型为 K、值类型为 V 的字典对象类型。
ini
type ScoreMap = Record<string, number>;
// 等价于:{ [key: string]: number }
const scores: ScoreMap = {
math: 95,
english: 88
};
使用场景:不需要提前定义具体属性名,只要约束键和值的类型,比如映射表、字典结构。
7. 工具类型对比总结表
表格
| 工具类型 | 操作对象 | 核心作用 |
|---|---|---|
keyof T |
对象类型 | 取出所有属性名,返回联合类型 |
Pick<T,K> |
对象类型 | 挑选保留指定属性 |
Omit<T,K> |
对象类型 | 剔除删除指定属性 |
Partial<T> |
对象类型 | 所有属性变为可选 |
Exclude<T,U> |
联合类型 | 排除联合中的指定项 |
ReturnType<T> |
函数类型 | 提取函数返回值类型 |
Record<K,V> |
- | 快速生成键值字典类型 |
三、写在最后
Docker 和 TypeScript 都是那种 "学会了每天用,没学会天天踩坑" 的工具。Docker 帮我们快速搭建一致的开发环境,告别 "我本地能跑";TS 工具类型帮我们用更优雅的方式复用类型,写出更安全、更易维护的代码。