这篇文章梳理了 TypeScript 中几个最实用的工具类型,以及 CSS 中经典的圣杯/双飞翼布局(三列自适应),都是面试官最爱问的。
第一部分:TypeScript 高级工具类型(Pick、Omit、Partial、Record、ReturnType、Exclude)
1. Pick ------ 从类型中挑选你想要的字段
假设有一个 User 接口,包含 id、name、age、email。在列表页我们只需要展示 id 和 name,这时用 Pick 生成一个新类型。
typescript
ini
interface User {
id: number;
name: string;
age: number;
email: string;
}
// 只挑选 id 和 name
type UserPreview = Pick<User, "id" | "name">;
const u: UserPreview = {
id: 1,
name: "张三",
// age 和 email 不能写,否则报错
};
2. Omit ------ 排除掉不需要的字段
有些敏感场景(比如返回给前端的用户信息)要去掉 email 或 password,用 Omit 最方便。
typescript
ini
// 去掉 email 字段
type UserSafe = Omit<User, "email">;
const safeUser: UserSafe = {
id: 2,
name: "李四",
age: 20,
// email 被排除了,不能写
};
3. Partial ------ 所有字段变为可选
常用于更新接口(PATCH 请求),客户端只传需要修改的字段。
typescript
ini
type PartialUser = Partial<User>;
// 可以只传部分字段
const patchUser: PartialUser = {
id: 3,
name: "王五",
age: 22,
};
// 甚至可以传空对象
const emptyObj: PartialUser = {};
4. Record ------ 构建键值对映射
非常适合做状态码、错误码的映射表。
typescript
typescript
// 键是 string,值是 number 的字典
type Dict = Record<string, number>;
const obj: Dict = { a: 1, b: 2 };
// HTTP 状态码映射
type ErrorMsgMap = Record<number, string>;
const errorMessage: ErrorMsgMap = {
400: "请求参数错误",
401: "未授权",
403: "权限不足,禁止访问",
404: "资源找不到",
500: "服务器内部错误",
};
function getErrMsg(code: number): string {
return errorMessage[code] ?? "未知错误";
}
5. ReturnType ------ 提取函数的返回值类型
typescript
csharp
function fn() {
return { x: 1, y: 2 };
}
type FnReturn = ReturnType<typeof fn>; // { x: number; y: number }
6. Exclude ------ 从联合类型中剔除某些成员
Exclude 是 Omit 的底层依赖,它处理的是联合类型 ,而 Omit 处理的是对象接口。
typescript
ini
type All = "id" | "name" | "age" | "email";
type AfterExclude = Exclude<All, "email">; // "id" | "name" | "age"
🔥 高频面试题:Omit<T, K> 的内部实现原理
很多面试官会问:"Omit 是怎么实现的?请你手写一个。"
答案 :Omit<T, K> 等价于 Pick<T, Exclude<keyof T, K>>。
拆解步骤:
keyof T获取T的所有键,得到联合类型。Exclude<联合类型, K>从联合中剔除K,剩下的就是要保留的键。Pick<T, 剩下的键>从T中挑选这些键,得到新类型。
我们自己实现一个 MyOmit:
typescript
scala
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// 测试
type MyOmitUser = MyOmit<User, "email">;
// 等价于 { id: number; name: string; age: number; }
第二部分:CSS 经典三列布局(Flex 实现 + BFC 理解)
PC 端最常见的一种布局:左右固定宽度,中间自适应 ,且中间内容要优先加载(HTML 结构上 main 写在最前面)。
为什么中间要先加载?
因为中间是核心内容,用户最想看到。左右两侧往往是广告、导航等次要信息,可以后加载。这叫做结构优先。
HTML 结构(关键)
html
css
<div class="layout">
<main class="content">Main Content</main>
<aside class="sidebar left">Left</aside>
<aside class="sidebar right">Right</aside>
</div>
注意:main 写在最前面,但视觉上它要在中间。我们需要用 Flex 的 order 属性或 flex-flow 调整顺序。
CSS(Flex 方案)
css
css
.layout {
display: flex; /* 开启 Flex 格式化上下文(FFC) */
flex-direction: row; /* 默认行方向 */
min-height: 100vh; /* 撑满视口高度,方便演示 */
}
.sidebar {
width: 200px; /* 左右固定宽度 */
background: #f0f0f0;
}
.left {
order: -1; /* 将左侧移动到最前面(视觉左) */
}
.content {
flex: 1; /* 中间占据剩余空间,自适应 */
background: #fff;
padding: 20px;
}
.right {
/* order 默认 0,排在 content 后面 */
}
关于 BFC 的拓展理解
笔记里提到了 BFC(块级格式化上下文)。当你给 .layout 设置 display: flex 时,它其实创建了一个 FFC(Flex Formatting Context) ,这类似于 BFC 的一种变体。
- BFC:块级元素垂直排列,行内元素水平排列,这是最基础的文档流。
- FFC:flex 子项按照主轴方向排列,且可以灵活伸缩。
- 为什么要"开启新的格式化上下文"?
因为外部的 BFC 规则(比如浮动、边距折叠)不会影响到内部的 flex 布局,反之亦然。这样布局更加可控,不会产生意外的样式干扰。
其他实现方式
- Grid :
display: grid; grid-template-columns: 200px 1fr 200px;更简洁。 - Float:传统方式,但需要清除浮动,已不推荐。
面试时如果问到"三列布局",Flex 和 Grid 都是加分项,但 Flex 兼容性更好,是首选。
总结
这两部分内容虽然主题不同,但都是前端进阶的基石:
- Docker 让你掌握部署与运行环境管理,从"纯写页面"走向"工程化"。
- TS 高级类型 让你的代码更健壮,类型推导更精准,写出的库更通用。
- CSS 三列布局 是页面架构的基本功,理解 BFC/FFC 能帮你避开无数样式坑。