前端面试高频考点 —— TS 高级类型 & CSS 三列布局

这篇文章梳理了 TypeScript 中几个最实用的工具类型,以及 CSS 中经典的圣杯/双飞翼布局(三列自适应),都是面试官最爱问的。


第一部分:TypeScript 高级工具类型(Pick、Omit、Partial、Record、ReturnType、Exclude)

1. Pick ------ 从类型中挑选你想要的字段

假设有一个 User 接口,包含 idnameageemail。在列表页我们只需要展示 idname,这时用 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 ------ 排除掉不需要的字段

有些敏感场景(比如返回给前端的用户信息)要去掉 emailpassword,用 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 ------ 从联合类型中剔除某些成员

ExcludeOmit 的底层依赖,它处理的是联合类型 ,而 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>>

拆解步骤:

  1. keyof T 获取 T 的所有键,得到联合类型。
  2. Exclude<联合类型, K> 从联合中剔除 K,剩下的就是要保留的键。
  3. 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 布局,反之亦然。这样布局更加可控,不会产生意外的样式干扰。

其他实现方式

  • Griddisplay: grid; grid-template-columns: 200px 1fr 200px; 更简洁。
  • Float:传统方式,但需要清除浮动,已不推荐。

面试时如果问到"三列布局",Flex 和 Grid 都是加分项,但 Flex 兼容性更好,是首选。


总结

这两部分内容虽然主题不同,但都是前端进阶的基石:

  • Docker 让你掌握部署与运行环境管理,从"纯写页面"走向"工程化"。
  • TS 高级类型 让你的代码更健壮,类型推导更精准,写出的库更通用。
  • CSS 三列布局 是页面架构的基本功,理解 BFC/FFC 能帮你避开无数样式坑。
相关推荐
李剑一1 小时前
《牛来》火了?我用ThreeJs实现了一个简易版的,代码全送给你,文章最后附演示效果
前端
世界哪有真情2 小时前
AI 写代码两年多,我发现自己越来越"看不进去"了
前端·后端·ai编程
星栈2 小时前
被 Rust async 纠正的三个异步认知
前端·后端·rust
前端_刘师兄2 小时前
前端开发工程师转FAE工程师路线规划
前端
禁止摆烂_才浅2 小时前
前端 AI 面试题
前端·面试·ai编程
程序员老赵2 小时前
Docker 部署 ZLMediaKit:轻松搭建高性能流媒体服务平台
前端·javascript·后端
Liora_Yvonne2 小时前
为什么每次发版,总有用户看到白屏?
前端
风月说与山鬼2 小时前
四、浏览器存储
前端·vue.js
hunterandroid2 小时前
[Android 从零到一] ViewPager2 与 Fragment 生命周期协同:从预加载到状态一致性
android·前端