mcpField
T
typescript-dev

typescript-dev

github.com/tenequm开发4053 次安装5 天前更新
代码仓库

关于 typescript-dev

typescript-dev 是指导 TypeScript 前端开发的技能,围绕 Vite、React、Tailwind CSS、Biome、Vitest 和 Hono 提供协同配置与实践建议。重点说明工具交界处的常见问题,并展示端到端类型安全的应用搭建方式。

主要功能

  1. 01整合前端与后端开发栈
  2. 02配置 Vite 与 React Compiler
  3. 03采用 Tailwind CSS v4 和 shadcn/ui
  4. 04使用 Biome 格式化与检查代码
  5. 05通过 Vitest 编写测试
  6. 06用 Hono RPC 共享前后端类型

适用场景

  1. 01搭建 React 与 TypeScript 应用
  2. 02排查 Vite 插件顺序和热更新问题
  3. 03设置 Tailwind CSS v4 样式与主题
  4. 04连接 Hono API 与 React 客户端

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tenequm/skills --skill typescript-dev

安装后在 Claude Code 中输入/typescript-dev确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CHANGELOG.md
LICENSE.txt
references/
biome.md
hono.md
react.md
shadcn.md
tailwind.md
typescript.md
vite.md
vitest.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)。

目标版本

工具版本说明
Vite8.3.3Rolldown 是唯一的默认打包器
@vitejs/plugin-react6.1.2v6 移除了内联的 babel 选项
React / react-dom19.3.0React Compiler 已稳定(1.0)
babel-plugin-react-compiler1.0.0使用 --save-exact 锁定版本
TypeScript7.0.2原生 Go 实现,速度约快 10 倍;不附带 JS API(兼容路径见 typescript.md 中的 6.0 说明)
Tailwind CSS4.3.3CSS 优先配置,无 JS 配置文件
shadcn/ui CLI4.21.3Base UI 是默认底层方案;cn 是独立的包
Biome2.5.15用于 lint、格式化和导入管理的单一二进制文件
Vitest5.0.3原生支持 Vite 的测试运行器;复用 vite.config
Hono4.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 的后者优先规则就能让调用方覆盖默认值。

最佳实践

  1. 让编译器进行优化。 编写简单的组件和计算;只有在需要让某个值成为稳定的 effect 依赖项时,才使用 useMemo/useCallback。
  2. 使用判别联合类型对状态建模,而不是使用松散的布尔值({ status: "loading" } | { status: "error"; error }),这样就无法表示不可能的状态。
  3. 使用 React.ComponentProps<"el"> 扩展原生属性,而不是手动重新声明 HTML 属性。
  4. 优先使用 use(),而不是 useContext()——它可以在提前返回后和条件语句中使用。
  5. 只使用语义化颜色令牌,并始终将 bg-* 与对应的 text-*-foreground 配对使用。
  6. biome check --write 是你本地唯一需要的命令;在流水线中使用 biome ci。
  7. Rolldown 是 Vite 8 的默认打包器——无需启用;使用 Rolldown 的 codeSplitting 拆分稳定的供应商代码(参见 vite.md)。
  8. 为会重写代码的工具锁定精确版本(babel-plugin-react-compiler、@biomejs/biome),避免不同版本发布导致意外差异。
  9. 不要将密钥放在客户端——只有带 VITE_ 前缀的环境变量会通过 import.meta.env 传入浏览器代码。
  10. 通过 vite.config.ts 进行测试——Vitest 会复用构建配置,因此测试使用相同的别名和转换;在 CI 中运行 vitest run,组件测试使用 jsdom。

资源

评论

支持 Markdown