关于 typescript-dev
typescript-dev 是指导 TypeScript 前端开发的技能,围绕 Vite、React、Tailwind CSS、Biome、Vitest 和 Hono 提供协同配置与实践建议。重点说明工具交界处的常见问题,并展示端到端类型安全的应用搭建方式。
主要功能
- 01整合前端与后端开发栈
- 02配置 Vite 与 React Compiler
- 03采用 Tailwind CSS v4 和 shadcn/ui
- 04使用 Biome 格式化与检查代码
- 05通过 Vitest 编写测试
- 06用 Hono RPC 共享前后端类型
适用场景
- 01搭建 React 与 TypeScript 应用
- 02排查 Vite 插件顺序和热更新问题
- 03设置 Tailwind CSS v4 样式与主题
- 04连接 Hono API 与 React 客户端
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tenequm/ skills --skill typescript-dev安装后在 Claude Code 中输入/typescript-dev确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CHANGELOG.mdLICENSE.txtreferences/biome.mdhono.mdreact.mdshadcn.mdtailwind.mdtypescript.mdvite.mdvitest.md详细介绍
TypeScript 前端开发
用于构建类型安全 TypeScript 应用的一体化技术栈:Vite 8(构建 + 开发服务器,由 Rolldown 驱动)、采用 React Compiler 的 React 19.3、TypeScript 7.0(严格模式,原生 Go 实现)、用于样式设计的 Tailwind CSS v4.3 + shadcn/ui、用于 lint 和格式化的 Biome 2.5、用于测试的 Vitest 5,以及用于后端/边缘 API 的 Hono 4。这些组件经过设计,能够相互配合——本技能介绍它们如何连接,以及横跨多个组件的易踩坑点。Hono 的 RPC 客户端(hc)可直接与 React 前端共享服务端类型,因此前后端无需代码生成即可实现端到端类型安全。
以下正文介绍跨工具层:这些工具组合使用时容易出问题的规则,以及一套可运行的端到端配置。每种工具也都有深入参考文档——请阅读你需要的文档:
- references/vite.md - Vite 8 配置、开发服务器、代理、HMR、Rolldown、代码拆分、构建优化、部署。
- references/react.md - React 19 模式:Actions、
use()、Activity、<ViewTransition>、Fragment refs、useEffectEvent、文档元数据以及 React Compiler。 - references/typescript.md - 严格的 TypeScript 7.0 配置和模式:tsconfig 默认值、适用于 API 消费工具的 6.0 兼容路径、泛型、
import defer。 - references/tailwind.md - Tailwind CSS v4 CSS 优先配置、OKLCH 主题、暗色模式、v4.3 实用类。
- references/shadcn.md - shadcn/ui CLI、使用 CVA +
data-slot编写组件、cn包、注册表、Base UI / Radix / React Aria。 - references/biome.md - Biome 配置、
biome check、domains、类型感知 lint、GritQL、从 ESLint/Prettier 迁移。 - references/vitest.md - Vitest 5 配置、Testing Library、模拟、覆盖率、浏览器模式、项目、v4->v5 迁移、测试速度。
- references/hono.md - Hono 4 Web 框架:路由、上下文、中间件、验证(Zod)、端到端类型安全 RPC、OpenAPI、辅助工具,以及多运行时部署(Workers/Node/Bun/Deno)。
目标版本
| 工具 | 版本 | 说明 |
|---|---|---|
| Vite | 8.3.3 | Rolldown 是唯一的默认打包器 |
| @vitejs/plugin-react | 6.1.2 | v6 移除了内联的 babel 选项 |
| React / react-dom | 19.3.0 | React Compiler 已稳定(1.0) |
| babel-plugin-react-compiler | 1.0.0 | 使用 --save-exact 锁定版本 |
| TypeScript | 7.0.2 | 原生 Go 实现,速度约快 10 倍;不附带 JS API(兼容路径见 typescript.md 中的 6.0 说明) |
| Tailwind CSS | 4.3.3 | CSS 优先配置,无 JS 配置文件 |
| shadcn/ui CLI | 4.21.3 | Base UI 是默认底层方案;cn 是独立的包 |
| Biome | 2.5.15 | 用于 lint、格式化和导入管理的单一二进制文件 |
| Vitest | 5.0.3 | 原生支持 Vite 的测试运行器;复用 vite.config |
| Hono | 4.13.13 | 基于 Web 标准的后端/边缘框架;v5 正在开发中 |
Node.js 22.12+ 是此技术栈实际支持的最低版本:Vite 单独使用时接受 20.19+,但 Vitest 5 和 @rolldown/plugin-babel 均要求 22.12+。
跨工具关键规则
这些规则之所以会以令人困惑的方式导致问题,正是因为它们处于两个工具的交界处。单个工具的详细信息见参考文档。
Vite 插件顺序:框架插件在前,react() 最后
当框架插件(TanStack Router/Start 等)生成路由或转换代码时,它必须先于 @vitejs/plugin-react 运行,这样 React 的 Fast Refresh 转换才能看到最终输出。顺序错误会导致路由生成失败和 HMR 损坏。
plugins: [
tanstackStart(), // or tanstackRouter() for SPA - framework first
tailwindcss(),
react(), // React plugin last among framework plugins
]
React Compiler 可替代手动记忆化,并改变 Vite 的接入方式
React Compiler 1.0 会在构建时自动记忆化组件、计算结果和回调。编写普通组件即可;不要使用 useMemo/useCallback/memo。关键在于 Vite 的接入点:@vitejs/plugin-react v6 移除了内联 babel 选项,因此旧的 react({ babel: { plugins: [...] } }) 接入方式已不再有效。现在,编译器通过单独的 Babel 插件运行:
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
plugins: [react(), babel({ presets: [reactCompilerPreset()] })]
安装:pnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler @types/babel__core。
plugin-react 6.1 还提供了一种实验性的原生(Rust)编译器路径:react({ compiler: true }),完全跳过 Babel——其安装注意事项请参阅 react.md。上面的 Babel 接入方式仍是稳定方案。
这也会影响 Biome:useExhaustiveDependencies 无法判断编译器是否在为你处理依赖项,因此大多数编译器用户都会将其关闭(参见 biome.md)。
Tailwind v4 以 CSS 为先——不再有 tailwind.config.js
Tailwind v4 通过 CSS 中的 @theme、@utility、@plugin、@source 配置所有内容。切勿创建或查找 tailwind.config.js/.ts。Vite 集成使用 @tailwindcss/vite 插件(也不需要 PostCSS 配置)。如果在 v4 项目中发现 tailwind.config.js,那就是遗留文件——将其删除,并把其中的值迁移到 CSS 中。详情请参阅 tailwind.md。
使用语义化令牌设置样式,切勿使用原始调色板或动态类名
<div className="bg-primary text-primary-foreground"> // respects theme + dark mode
<div className="bg-blue-500 text-white"> // breaks theming - avoid
也切勿通过片段拼接类名(bg-${color}-500)——Tailwind 扫描器只能识别完整的字面字符串,因此动态名称不会生成任何 CSS,而且不会提示。请使用包含完整类字符串的查找映射。
TypeScript 7.0:善用新的默认设置,并注意它没有 JS API
TS 6.0 和 7.0 内置了许多过去需要手动设置的选项:strict 和 noUncheckedSideEffectImports 默认启用,因此在新的 tsconfig 中无需再写。若忽略两个默认设置,Vite 应用就会出问题:types 默认为 [],且现在会检查副作用导入——因此,在添加 "types": ["vite/client"] 之前,import "./styles.css" 会报错(TS2882),而 import.meta.env 的类型也无法识别(TS2339)(按需追加 "node" 等)。baseUrl 已弃用——请使用带前缀的 paths。
现在,直接运行 pnpm add -D typescript 会安装 7.0。该版本提供 tsc 二进制文件,但不提供编程 API(要到 7.1 才会恢复)。这套技术栈没有问题——Biome、plugin-react 和 shadcn CLI 都不会导入 typescript——但 typescript-eslint、框架检查工具(Vue、Astro、Svelte、MDX)以及其他使用 import ts from "typescript" 的工具,都需要官方的 6.0 别名组合。请参阅 typescript.md。
只需一条 Biome 命令,且唯一的 include 键是 files.includes
运行 biome check(或 biome ci)——它会在一次处理中完成格式化、代码检查和导入排序;切勿将其拆分成单独的 lint+format 调用。在 Biome 2.x 中,唯一的文件选择键是 files.includes(带 s);files.ignore/files.include/files.exclude 均不存在,使用时会报错 Found an unknown key。使用否定模式排除文件:"includes": ["**", "!**/routeTree.gen.ts"]。更多内容请参阅 biome.md。
Hono RPC 将后端类型与 React 前端绑定在一起,确保二者保持同步
当 API 使用 Hono 时,React 应用会通过 hc<AppType>() 客户端与其通信,该客户端会直接导入服务端导出的 typeof app。这个共享类型就是两端的衔接点:只有两端运行相同版本的 Hono,且两端的 tsconfig.json 都设置了 "strict": true,它才能正常工作(不一致会导致 "Type instantiation is excessively deep" 错误)。这个衔接点还有两条容易踩坑的规则:处理器必须指定状态码(c.json(data, 200)),客户端才能推断响应类型;客户端调用的路由不能使用 c.notFound()。路由数量增长后,应只编译一次客户端类型(hcWithType),以保持 IDE 响应流畅。运行 pnpm why hono,检查是否有适配器的 peer 范围引入了第二份副本。如果 Hono 在 Vite 开发服务器后处理 CORS,请在 vite.config.ts 中设置 server.cors: false,避免两层 CORS 相互冲突。完整详情请参阅 hono.md。
端到端配置
一个精简但完整的 React + TypeScript + Tailwind + Biome 项目。根据你的路由器或 SSR 选择替换框架插件(TanStack 和 Cloudflare 变体请参阅 vite.md)。
vite.config.ts
import { defineConfig } from 'vite'
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
tailwindcss(),
react(),
babel({ presets: [reactCompilerPreset()] }),
],
resolve: {
alias: { '@': new URL('./src', import.meta.url).pathname },
},
})
import.meta.url 是符合 ESM 规范的路径解析方式——ESM 配置中没有 __dirname,而 Vite 配置仅支持 ESM。
tsconfig.json(TypeScript 7.0,也适用于 6.0)
{
"compilerOptions": {
// strict + noUncheckedSideEffectImports are ON by default since 6.0 - omitted on purpose
"target": "es2023",
"module": "preserve",
"moduleResolution": "bundler",
"moduleDetection": "force",
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"erasableSyntaxOnly": true,
"skipLibCheck": true,
"noEmit": true,
"types": ["vite/client"],
"paths": { "@/*": ["./src/*"] }
},
"include": ["src"]
}
对于由 Vite 打包的应用,module: preserve + moduleResolution: bundler 是正确的搭配;只有用于 Node 执行的代码才应改用 nodenext。"types": ["vite/client"] 至关重要:它会声明 CSS/资源模块和 import.meta.env,否则默认启用的副作用导入检查会拒绝 import "./styles.css"。其他全局类型只有在你将它们列出时才会生效("node"、"vitest/globals")。exactOptionalPropertyTypes 意味着可能接收 undefined 的可选属性必须声明为 prop?: T | undefined。
biome.json
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": { "includes": ["**", "!**/components/ui", "!**/routeTree.gen.ts"] },
"formatter": { "enabled": true, "indentStyle": "space", "lineWidth": 100 },
"linter": {
"enabled": true,
"rules": { "preset": "recommended" },
"domains": { "react": "recommended" }
},
"javascript": { "formatter": { "quoteStyle": "double" } },
"assist": { "enabled": true, "actions": { "source": { "organizeImports": "on" } } }
}
src/styles.css
@import "tailwindcss";
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--radius: 0.5rem;
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
}
@import "tailwindcss"; 这一行至关重要:若缺少它,单靠 @tailwindcss/vite 插件不会生成任何样式——漏掉导入是导致“Tailwind 什么都不渲染”的典型陷阱。对于引用 CSS 变量的令牌,请使用 @theme inline(而非普通的 @theme),这样它们才能随深色模式变化而更新。
符合整个技术栈预期的组件写法
使用普通函数,将 ref 作为常规属性(无需 forwardRef),通过 React.ComponentProps 使用原生元素属性,通过 CVA 定义变体,使用 data-slot 作为样式钩子,并且不手动进行记忆化——编译器会处理。
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
outline: "border border-input bg-background hover:bg-accent",
ghost: "hover:bg-accent hover:text-accent-foreground",
},
size: { default: "h-9 px-4 py-2", sm: "h-8 px-3", lg: "h-10 px-8" },
},
defaultVariants: { variant: "default", size: "default" },
}
)
function Button({
className,
variant,
size,
ref,
...props
}: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants>) {
return (
<button
ref={ref}
data-slot="button"
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
)
}
注意 cn() 的顺序:默认值在前,调用方的 className 在后,这样 tailwind-merge 的后者优先规则就能让调用方覆盖默认值。
最佳实践
- 让编译器进行优化。 编写简单的组件和计算;只有在需要让某个值成为稳定的 effect 依赖项时,才使用
useMemo/useCallback。 - 使用判别联合类型对状态建模,而不是使用松散的布尔值(
{ status: "loading" } | { status: "error"; error }),这样就无法表示不可能的状态。 - 使用
React.ComponentProps<"el">扩展原生属性,而不是手动重新声明 HTML 属性。 - 优先使用
use(),而不是useContext()——它可以在提前返回后和条件语句中使用。 - 只使用语义化颜色令牌,并始终将
bg-*与对应的text-*-foreground配对使用。 biome check --write是你本地唯一需要的命令;在流水线中使用biome ci。- Rolldown 是 Vite 8 的默认打包器——无需启用;使用 Rolldown 的
codeSplitting拆分稳定的供应商代码(参见 vite.md)。 - 为会重写代码的工具锁定精确版本(
babel-plugin-react-compiler、@biomejs/biome),避免不同版本发布导致意外差异。 - 不要将密钥放在客户端——只有带
VITE_前缀的环境变量会通过import.meta.env传入浏览器代码。 - 通过
vite.config.ts进行测试——Vitest 会复用构建配置,因此测试使用相同的别名和转换;在 CI 中运行vitest run,组件测试使用jsdom。
资源
- Vite:https://vite.dev/guide/ - Vite 8 博客:https://vite.dev/blog/announcing-vite8
- React 19.3:https://react.dev/blog/2026/09/09/react-19-3 - 编译器:https://react.dev/learn/react-compiler
- TypeScript 7.0:https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/
- Vitest 5:https://vitest.dev/blog/vitest-5 - Hono:https://hono.dev/docs/
- Tailwind CSS:https://tailwindcss.com/docs - shadcn/ui:https://ui.shadcn.com/docs
- Biome:https://biomejs.dev/