TypeScript React.FC如何区分用户传入与组件内部最终Props
React.FC 原生不支持同时传入「用户侧传入的预期Props类型」和「组件内部实际拿到的最终Props类型」两个泛型参数。
从其官方类型定义可见,React.FC 仅接收一个泛型参数P,该参数会同时约束三个维度:组件调用方的入参类型校验与提示、组件函数入参的props类型、defaultProps的合法值范围(要求为Partial<P>),没有预留第二个泛型位用来区分内外使用的不同Props类型。
额外定义FinalCompProps并在组件内部做类型断言的方案虽然能快速消除报错,但会绕过TS的类型校验,后续修改Props定义时容易出现漏判,两类典型场景都有更稳妥的实现方式:
场景1:defaultProps导致可选属性报undefined错误
这类问题不需要额外定义内部Props类型,直接在参数解构时设置默认值即可,TS可以自动推导对应属性不会为undefined:
import React, { FC } from "react"; interface CompProps { name?: string; } const Comp: FC<CompProps> = (props) => { // 解构时声明默认值,无需额外类型断言 const { name = "" } = props; return <div>{name.toUpperCase()}</div>; }; Comp.defaultProps = { name: "", };
如果项目不需要兼容React 16及更早的旧版本,可以直接移除类组件时代遗留的defaultProps配置,完全依靠函数参数解构默认值实现默认属性逻辑,TS的类型推导会更顺畅,不会出现类型与实际运行值不一致的问题。
场景2:React.cloneElement隐式注入props
这类场景的核心是组件Props天然分为两部分:一部分是父组件自动注入、不需要外部传入的,另一部分是使用组件时需要手动传入的。不需要在组件函数内部做props强转,只需要在导出层做类型收敛即可:
import React, { FC } from "react"; // 父组件通过cloneElement自动注入的Props类型 interface InjectedProps { traceId: string; } // 外部调用组件时需要传入的Props类型 interface ExternalProps { name?: string; } // 组件内部实际可访问的完整Props类型 type InternalCompProps = InjectedProps & ExternalProps; // 内部实现使用完整Props类型,所有属性都有正确类型提示 const CompInternal: FC<InternalCompProps> = (props) => { const { name = "", traceId } = props; return <div data-traceid={traceId}>{name.toUpperCase()}</div>; }; CompInternal.defaultProps = { name: "", }; // 对外导出的组件仅校验外部传入的Props,不会要求传入父组件注入的字段 export const Comp = CompInternal as FC<ExternalProps>;
这种写法的类型边界更清晰:组件内部开发时可以获得完整的类型校验,外部调用组件时TS只会提示需要手动传入的属性,不会被注入字段的类型要求干扰,也避免了内部强转props带来的类型安全隐患。
内容的提问来源于stack exchange,提问作者Eliav Louski

