ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

一条评论的旅程:nelsonlai.dev 的 TypeScript 高级特性类型安全实践指南

一条评论的旅程:nelsonlai.dev 的 TypeScript 高级特性类型安全实践指南 一条评论的旅程nelsonlai.dev 的 TypeScript 高级特性类型安全实践指南【免费下载链接】nelsonlai.devMy personal website built with TypeScript, Next.js, and TailwindCSS项目地址: https://gitcode.com/gh_mirrors/ho/nelsonlai.devnelsonlai.dev 是一个基于 Next.js TypeScript 构建的个人站点。这篇文章顺着一条评论从数据库流向页面的路线讲清楚 TypeScript 高级特性是怎么让类型安全贯穿全程的。读完你能避开两个大坑改了字段名没人提醒、前端盲猜后端返回结构。两个翻车现场改个字段名为什么没人提醒第一个现场后端把字段avatarUrl改成image前端照样编译通过上线后头像一片裂图。第二个现场前端对着接口文档猜返回结构写了一堆if (data.xxx ! undefined)的防御代码后端改个字段名页面直接白屏。病根都一样数据跨层流动但长什么样只存在于口头约定里。TypeScript 高级特性要做的就一件事——把口头约定变成代码让编译器在你上线前先开口。这也是 TS 最佳实践里最划算的一步把该人肉盯的活儿交给编译器。第一站·数据出生地 3 行代码锁定表字段类型评论表定义在 src/db/schemas/comment.schema.ts。Drizzle 建表像画数据形状的图纸字段类型、是否可空、外键约束全写死在代码里节选export const comments pgTable(comments, { body: text(body).notNull(), userId: text(user_id).notNull().default(ghost).references(() users.id), postId: text(post_id).notNull().references(() posts.slug), parentId: text(parent_id), })从这一刻起代码里任何碰这张表的地方编译器都知道body必须是字符串、userId匿名时兜底为ghost、postId外键删文章会级联删评论。评论还没出生类型安全已经锁了第一道。第二站·数据过安检 输入严格输出克制数据要出数据库、走 API这站的规矩就八个字输入严格输出克制。输入侧Zod 校验像机场安检不在清单上的东西门都进不了。src/orpc/schemas/comment.schema.ts 里评论接口的slug是z.string().min(1)sort只接受newest | oldest且给了默认值。前端传错参数业务逻辑执行前就被拦下不用在代码里写防御式if。输出侧用 Pick。Pick 像点菜只挑客人要的几样。接口要带用户信息但只点四个字段user: createSelectSchema(users).pick({ id: true, name: true, image: true, role: true, })email这类不该外流的字段天然不存在。更妙的是oRPC 直接从 Zod schema 推断出 TS 类型前端拿到的后端会返回什么不再是猜的是编译器盖章的合同。第三站·数据上舞台 泛型与联合类型让一个组件吃下所有数据组件层是泛型、联合类型、类型守卫的主场。泛型像个占位符组件写一次每处业务塞自己的形状。管理后台的表格列定义评论表是ColumnDefComment用户表是ColumnDefUser见 src/components/tables/users/columns.tsx同一个表格组件通吃。而且Comment类型不是手写的是从 oRPC 输出类型里推断出来的——源头一变全链路跟着变。联合类型描述可能是 A也可能是 B。博客里评论和回复共用同一张表靠parentId是否为空区分接口参数也写成type: comments | replies传别的一律拒绝。src/components/ui/link.tsx 里的 Link 组件是教科书案例联合类型、条件类型、类型守卫一次看全节选type InternalLink / | /${string} type ExternalLink http://${string} | https://${string} type ValidLink InternalLink | ExternalLink export function LinkTHref extends ValidLink(props: LinkPropsTHref) { if (isExternalLink(props)) { return a href{props.href} target_blank relnoopener noreferrer / } return LocalizedLink {...props} / }isExternalLink就是类型守卫运行时看 href 前缀做判断编译期把 props 收窄成对应的形状后续代码自动获得正确的自动补全。传一个非法链接组件直接抛错错误在舞台上现形而不是流到用户手里。收获小结哪个特性解决什么问题特性解决的问题什么时候该用Drizzle 类型化建表字段类型与约束在出生地锁死杜绝拼错字段名定义数据库表、外键、索引时Zod 校验输入在进业务逻辑前被运行时验证每个 API 的入参、出参Pick输出只带必需字段隐私字段天然不外流裁剪用户信息等嵌套输出时泛型一个组件/函数适配多种数据形状表格、列表、Link 这类通用 UI联合类型 类型守卫表达可能是 A 也可能是 B运行时收窄区分评论/回复、站内/站外链接上手指南clone 下来从哪读起git clone https://gitcode.com/gh_mirrors/ho/nelsonlai.dev建议从 src/db/schemas/comment.schema.ts 读起看数据出生地再到 src/orpc/schemas/comment.schema.ts 看过安检最后到 src/orpc/procedures.ts 看 schema 是怎么绑到接口上的。顺着一条评论走完这三个文件文中这些高级特性就都亲手摸过了。【免费下载链接】nelsonlai.devMy personal website built with TypeScript, Next.js, and TailwindCSS项目地址: https://gitcode.com/gh_mirrors/ho/nelsonlai.dev创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表