React + TypeScript 组件 Props 类型标注实战:基础类型语法、React 专属类型与 type/interface 选型指南 React TypeScript 组件 Props 类型标注实战基础类型语法、React 专属类型与 type/interface 选型指南【免费下载链接】reactCheatsheets for experienced React developers getting started with TypeScript项目地址: https://gitcode.com/gh_mirrors/reactt/react-typescript-cheatsheet本文基于 react-typescript-cheatsheet 仓库中的 Typing Component Props 文档展开系统讲解在 React TypeScript 项目中为组件 Props 做类型标注的完整知识体系从最常用的基础 TypeScript 类型字面量联合、字典类型、事件回调、状态 setter 传递到最容易误解的object、{}等类型再到ReactNode、CSSProperties、ComponentProps*等 React 专属类型的正确用法最后给出 type 与 interface 的选型结论与对照表。读完后你可以为任意函数式/类组件写出可复用、可被 IDE 良好推断和报错的类型定义。该文档在仓库中被 Docusaurus 站点配置 设为导航栏 Learn 的入口页是整个 Getting Started 系列的起点Introduction 中声明了本文档的适用前提具备 React 与 TypeScript 基础类型知识且假设使用最新版本的 React 与 TypeScript文中 React 19 相关行为均以此为准。一、基础 Prop 类型速查React TS 项目中会反复用到的类型文档开篇给出了一份你大概率会用到的 TypeScript 类型清单以下代码完整保留了原文档的示例与注释是全文的核心骨架type AppProps { message: string; count: number; disabled: boolean; /** array of a type! */ names: string[]; /** string literals to specify exact string values, with a union type to join them together */ status: waiting | success; /** an object with known properties (but could have more at runtime) */ obj: { id: string; title: string; }; /** array of objects! (common) */ objArr: { id: string; title: string; }[]; /** any non-primitive value - cant access any properties (NOT COMMON but useful as placeholder) */ obj2: object; /** an interface with no required properties - (NOT COMMON, except for things like React.Component{}, State) */ obj3: {}; /** a dict object with any number of properties of the same type */ dict1: { [key: string]: MyTypeHere; }; dict2: Recordstring, MyTypeHere; // equivalent to dict1 /** function that doesnt take or return anything (VERY COMMON) */ onClick: () void; /** function with named prop (VERY COMMON) */ onChange: (id: number) void; /** function type syntax that takes an event (VERY COMMON) */ onChange: (event: React.ChangeEventHTMLInputElement) void; /** alternative function type syntax that takes an event (VERY COMMON) */ onClick(event: React.MouseEventHTMLButtonElement): void; /** any function as long as you dont invoke it (not recommended) */ onSomething: Function; /** an optional prop (VERY COMMON!) */ optional?: OptionalType; /** when passing down the state setter function returned by useState to a child component. number is an example, swap out with whatever the type of your state */ setState: React.DispatchReact.SetStateActionnumber; };下面按场景分组解读这些类型的选择依据。字面量联合表达有限取值的 propstatus: waiting | success用字符串字面量联合限定 prop 只能取精确值而不是宽泛的string。这是 React 中最典型的受控枚举写法调用方传Comp statuswaiting /合法传Comp statusfoo /会直接报类型错误。对于需要多选一但取值可能来自外部的场景例如根据是否传href渲染 button 或 anchor可以进一步结合判别联合与类型收窄仓库的 Useful Patterns by Use Case 文档中有完整实现hasHref类型守卫 函数重载。对象与对象数组内联对象类型obj演示了内联对象字面量类型运行时可能有更多属性objArr是其数组形式——这是列表渲染objArr.map(...)中最常见的 props 形态。注意内联对象类型不能extend如果需要继承/扩展应改用具名 interface见第五节。字典类型{ [key: string]: T }与Recordstring, Tdict1与dict2是等价的都表示任意多个同类型属性的映射对象例如{ a: MyTypeHere; b: MyTypeHere }。当需要精确为空对象时则不能依赖{}正确姿势见第四节。回调函数 props四种写法对比写法适用场景onClick: () void无参回调最高频点击、关闭等onChange: (id: number) void带具名参数的回调例如 onChange 回传选中的 idonChange: (event: React.ChangeEventHTMLInputElement) void接收 React 合成事件的回调泛型参数是event.target的类型这是 Forms and Events 一节的标准做法onClick(event: React.MouseEventHTMLButtonElement): void方法签名风格的函数类型语法与箭头函数风格等价onSomething: Function只要不调用它就随便是什么函数文档明确标注not recommended仅在不关心签名的极少数占位场景使用可选 prop 与状态 setter 传递optional?: OptionalType可选属性VERY COMMON。注意 React 19 起函数组件不再支持defaultProps默认值应直接写在解构参数中见 You May Not Need defaultPropstype GreetProps { age?: number }; const Greet ({ age 21 }: GreetProps) { /* ... */ };setState: React.DispatchReact.SetStateActionnumber把useState返回的 setter 传给子组件时的标准类型。number只是示例应替换为你实际 state 的类型它同时支持setState(5)与setState(prev prev 1)两种调用形态SetStateActionT T | ((prev: T) T)。二、最容易误解的类型一objectobject是 TypeScript 中常见的误解来源。它不是任意对象而是**任意非原始类型**即除number、bigint、string、boolean、symbol、null、undefined之外的一切值——包括函数、数组、Date等。在 React 场景下你几乎不会需要表达任意非原始值因此object用得会很少它唯一的价值是作为无法确定具体形状时的占位符但代价是无法访问任何属性。能推断出具体形状时永远优先写明确的对象类型。三、最容易误解的类型二空接口{}与Object空接口、{}与Object三者都表示任意非 nullish 值而不是你以为的空对象。社区惯例typescript-eslint 的no-empty-interface、ban-types等规则都不推荐直接以它们作为 prop 类型interface AnyNonNullishValue {} // equivalent to type AnyNonNullishValue {} or type AnyNonNullishValue Object let value: AnyNonNullishValue; // these are all fine, but might not be expected value 1; value foo; value () alert(foo); value {}; value { foo: bar }; // these are errors value undefined; value null;这个宽到意外的特性正是它危险的根源你以为在约束必须是个对象实际只约束了非 null/undefined。四、React 专属 Prop 类型ReactNode、JSX.Element、CSSProperties与ComponentProps*文档给出的第二类示例面向接受其他 React 组件作为 props的组件export declare interface AppProps { children?: React.ReactNode; // best, accepts everything React can render childrenElement: React.JSX.Element; // A single React element style?: React.CSSProperties; // to pass through style props onChange?: React.FormEventHandlerHTMLInputElement; // form events! the generic parameter is the type of event.target props: Props React.ComponentPropsWithoutRefbutton; // to impersonate all the props of a button element and explicitly not forwarding its ref props2: Props React.ComponentPropsWithRefMyButtonWithForwardRef; // to impersonate all the props of MyButtonWithForwardRef and explicitly forwarding its ref }React.ReactNode与React.JSX.Element的区别原文档引用了社区维护者的解释合法的 React node 与React.createElement的返回值不是同一回事。无论组件最终渲染什么createElement永远返回一个对象即React.JSX.Element而React.ReactNode是组件所有可能返回值的集合React.JSX.Element→React.createElement的返回值React.ReactNode→ 组件的返回值可渲染内容的全集仓库的 ReactNode 参考页 进一步给出了ReactNode的完整成员构成ReactElement、string、number、bigint、boolean、null、undefined、IterableReactNode、ReactPortal以及在 React 19 异步 Server Components 下的PromiseReactNode。该参考页同时明确了两条实践结论与本文直接相关children应标ReactNode调用方可能传字符串、数组或nullReactNode才能全覆盖不要用ReactNode作为函数组件返回类型组件能返回什么比能接收什么范围更窄应让 TS 推断返回类型或显式标注React.JSX.Element/ReactElement。CSSProperties透传style的正确类型style?: React.CSSProperties是透传内联样式的标准写法。仓库的 CSSProperties 参考页 补充了几个容易踩坑的细节它继承自csstype的Propertiesstring | number全部标准 CSS 属性都有补全与取值校验长度类属性中数字会被解释为 pxwidth: 100→width: 100px字符串原样透传lineHeight、opacity、zIndex等少数属性是无单位的厂商前缀写 PascalCaseWebkitTransform、MozAppearance它刻意没有索引签名所以写 CSS 自定义属性style{{ --accent: tomato }}会报错参考页给出了三种解法as CSSProperties断言、CSSProperties { [key: \--${string}]: string | number }交叉类型、模块增强declare module react。ComponentPropsWithoutRef/ComponentPropsWithRef镜像 HTML 元素或组件的全部 props这是包装组件wrapper component的核心工具。仓库的 ComponentProps 参考页 将types/react提供的三个相关工具类型整理为类型含义ComponentPropsT组件或元素声明的 props 本身ComponentPropsWithRefT在ComponentPropsT基础上为类组件追加ref对 React 19 的函数组件结果与ComponentPropsT相同ref已是普通 propComponentPropsWithoutRefT从ComponentPropsT中剥掉ref避免透传 props 时ref泄漏版本适用前提React 19 通常只需要ComponentPropsTReact ≤18 则遵循转发 ref 用WithRef、不转发用WithoutRef。其典型实战是包装一个接收全部buttonprops 的Button完整示例来自仓库的 Wrapping/Mirroring 模式// usage function App() { // Type foo is not assignable to type button | submit | reset | undefined.(2322) // return Button typefoo sldkj /Button // no error return Button typebutton text /Button; } // implementation export interface ButtonProps extends React.ComponentPropsWithoutRefbutton { specialProp?: string; } export function Button(props: ButtonProps) { const { specialProp, ...rest } props; // do something with specialProp return button {...rest} /; }该模式文档同时解释了两个反面教材不要用React.HTMLProps底层使用AllHTMLAttributes会把type推断成过宽的string也不要用React.HTMLAttributes缺少type等元素专属属性。另外ComponentProps还可用于反查组件的 props 类型例如type IconName ComponentPropstypeof Icon[name]详见参考页的 Infer a specific prop type 小节。五、type 还是 interface为 Props 和 State 做类型标注时type 与 interface 都可以选哪个TL;DR社区流传的简明结论ortaUse Interface until You Need Type——默认用 interface直到确实需要 type 的能力为止。更具体的经验法则写库或第三方 ambient 类型定义公开 API时始终用interface使用方可以通过declaration merging在缺失时扩展你的定义React 组件的 Props 和 State 建议用type更一致且约束更强无法被外部意外合并进属性。TypeScript 官方手册同样提供了 type alias 与 interface 差异的说明。另有一个规模化的考量大型代码库中出于性能原因接口比交叉类型更利于 TS 服务端缓存有人偏好 interface这一观点在 Microsoft 的 TypeScript 性能 wiki 中有提及但原文档提示应结合实际情况审慎看待。能力上type 更适合联合类型如type MyType TypeA | TypeBinterface 更适合声明字典形状后再去extend/implement。Types vs Interfaces 对照表原文档附带的对照表来源Karol Majewski完整保留如下✅ 支持、⚠️ 部分场景支持、 不支持AspectTypeInterfaceCan describe functions✅✅Can describe constructors✅✅Can describe tuples✅✅Interfaces can extend it⚠️✅Classes can extend it✅Classes can implement it (implements)⚠️✅Can intersect another one of its kind✅⚠️Can create a union with another one of its kind✅Can be used to create mapped types✅Can be mapped over with mapped types✅✅Expands in error messages and logs✅Can be augmented✅Can be recursive⚠️✅⚠️ 表示部分场景下可行。原文档提醒这是nuanced话题不必过度纠结——日常按库用 interface、应用内 Props/State 用 type执行即可。六、小结与延伸阅读本篇覆盖了 basic-type-examples.md 的全部核心内容基础 props 类型速查表、object/{}/Object的语义澄清、React 专属类型ReactNode/JSX.Element/CSSProperties/ComponentProps*的选用边界以及 type vs interface 的完整对照结论并结合仓库参考文档补充了 React 19 下 ref 类型与ReactNode成员构成的最新细节。继续深入时可按以下仓库文档路径展开Introduction前提知识与 TypeScript 起步模板Vite / Next.js / Remix / ExpoFunction Components、HooksuseState、useCallback、useReducer等 Hook 的类型标注Useful Patterns by Use CaseWrapping/Mirroring、泛型组件、Render Props、按 prop 收窄类型等进阶模式forwardRef/createRef 与 Ref 参考页React 19 下ref作为普通 prop 的类型写法ReactNode、ComponentProps、CSSProperties本文引用的三个类型参考页。【免费下载链接】reactCheatsheets for experienced React developers getting started with TypeScript项目地址: https://gitcode.com/gh_mirrors/reactt/react-typescript-cheatsheet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考