技术文章/工程化
让类型自己说话:我如何组织一个中等规模项目的类型层
类型写到一定规模之后,问题就不再是"怎么写类型",而是"类型放在哪里、由谁负责"。
目录:三层就够
src/
├─ types/
│ ├─ api.ts # 接口出入参,与后端契约一一对应
│ ├─ model.ts # 领域模型,页面之间共享的那一份
│ └─ ui.ts # 纯展示态类型(Tab 枚举、表单状态)
绝大多数项目的类型混乱,根源是把接口返回体直接当成领域模型用。后端字段一改,半个页面的类型全红;更糟的是,前端会不知不觉依赖上后端那些本不该暴露的字段。
边界:在数据入口处收口
只在两个地方做类型转换:请求封装层、以及跨页面共享的状态层。
export async function fetchPost(slug: string): Promise<Post> {
const raw = await http.get<ApiPost>(`/posts/${slug}`)
return toPost(raw) // ← 唯一的转换点
}
function toPost(raw: ApiPost): Post {
return {
id: raw.id,
title: raw.title,
tags: raw.tags.map((t) => t.name),
publishedAt: new Date(raw.published_at),
}
}
好处是后端改字段时,你只需要改 toPost 一个函数,而不是满仓库搜索。
什么时候该放弃类型体操
我曾经写过一个能自动推导嵌套可选字段类型的小工具,用了三层条件类型,注释比实现长。半年后我自己都没看懂。
判断标准很简单:如果这段类型代码需要一个专门的注释来说明它在干什么,那它多半不该存在。
用 as 或者手写一个显式接口,都比让人看不懂的推导更好。
最后一点
开 strict,然后不要用 any 去关掉它的报错。真要临时放行,用 unknown + 一次显式的类型守卫,至少让下一个读到这段代码的人知道这里有风险。
评论
还没有评论,坐个沙发?