TypeScript React路由报错:缺少FormProps要求的toggleCreateForm属性
将应用重构为TypeScript后,使用React Router时遇到报错:
Property 'toggleCreateForm' is missing in type '{}' but required in type 'FormProps'
toggleCreateForm是在其他组件中声明、由自定义hook管理的状态,此前非TypeScript环境可正常运行,疑惑为何需要在路由元素中传递该props。
相关代码
App.tsx
function App(): React.JSX.Element { const [isLoading, setIsLoading] = useState(false); const token = useAppSelector(selectToken); const currentUser = useAppSelector(selectCurrentUser); const isAuthenticated = useAppSelector(selectAuthenticated); const dispatch = useAppDispatch(); const checkLoggedIn = useCallback(() => { setIsLoading(true) dispatch(userCheckLoggedIn()); setIsLoading(false) }, [isAuthenticated]); useEffect(() => { checkLoggedIn() }, [checkLoggedIn]) console.debug( "App", "currentUser", currentUser, "token=", token ) // Find current user using local storage "token" if (isLoading) { return <LoadingComponent/> } else { if (!(token && isAuthenticated)) { return ( <div className=""> <NavBar></NavBar> <Routes> <Route path="/register" element={<SignupForm/>}/> <Route path="/" element={<Homepage/>}/> <Route path="*" element={<Homepage/>}/> </Routes> </div> ) } else { return ( <div className="App"> <NavBar></NavBar> <Routes> <Route path="/workouts" element={<WorkoutList/>}/> <Route path="/workouts/:workout_id" element={<WorkoutDetail/>}/> <Route path="/workouts/add" element={<NewWorkoutForm/>}/> <Route path="/register" element={<SignupForm/>}/> <Route path="/exercises" element={<ExerciseList/>}/> <Route path="/exercises/add" element={<NewExerciseForm/>}/> <Route path="/account" element={<Account/>}/> <Route path="/" element={<Homepage/>}/> <Route path="*" element={<NotFound/>}/> </Routes> </div> ); } } } export default App;
NewWorkoutForm.tsx
interface FormValues { name: string, category: number }; interface Category { id: number, user_id: number name: string, systemdefault: boolean }; interface FormProps{ toggleCreateForm(): boolean } const NewWorkoutForm = ({toggleCreateForm} : FormProps): React.JSX.Element => { const dispatch = useAppDispatch(); const categories = useAppSelector(selectCategories); const currentUser = useAppSelector(selectCurrentUser); const categoryComponents : object[] = categories.map( (cat : Category) => ( <option value={cat.id}>{cat.name}</option> )) return ( <div className="d-flex justify-content-center"> <Card className="w-25 d-flex flex-column align-items-center pb-3"> <h3>New Workout Form</h3> <Formik initialValues={{ name: '', category: 1, }} validate={(values: FormValues) => { const errors: FormikErrors<FormValues> = {}; if (!values.name){ errors.name = 'Name Required'} return errors }} onSubmit={(values : FormValues, { setSubmitting } : FormikHelpers<FormValues>) => { setTimeout( async () => { dispatch(addWorkout({ ...values, user_id: currentUser.id, favorited: false })) setSubmitting(false); toggleCreateForm(); }, 400) }} > {({isSubmitting}) => ( <div> <Form> <div className="d-flex flex-column align-items-center row-gap-3"> <div className="d-flex flex-column align-items-between row-gap-3"> <div className="d-flex flex-row justify-content-between"> <label htmlFor="name">Workout Name:</label> <Field type='name' name='name'/> </div> <div style={{color: "red"}}> <ErrorMessage name='name' component='div'/> </div> <div className="d-flex flex-row justify-content-between"> <label htmlFor="category">Workout Category:</label> <Field as='select' name='category'> {categoryComponents} </Field> </div> <div style={{color: "red"}}> <ErrorMessage name='category' component='div'/> </div> </div> <div className="d-flex flex-row column-gap-5"> <button className="btn btn-danger" onClick={toggleCreateForm}>Cancel</button> <button className="btn btn-success" type='submit' disabled={isSubmitting}>Create Workout</button> </div> </div> </Form> </div> )} </Formik> </Card> </div> ) } export default NewWorkoutForm;
toggleCreateForm 相关代码
const [showCreateWorkoutForm, setShowCreateWorkoutForm] = useToggle(); const toggleCreateForm = () => { setShowCreateWorkoutForm() }
原因
TypeScript对组件Props做强类型校验:你给NewWorkoutForm定义了FormProps接口,其中toggleCreateForm是必填属性,但在App.tsx的路由中渲染<NewWorkoutForm/>时未传递该props,因此触发报错。
而JavaScript环境下无类型校验,即便缺传props也不会提前报错,但运行时若toggleCreateForm不存在,仍会抛出toggleCreateForm is not a function的错误,只是问题被延后暴露了。
解决方案
根据你的使用场景,可选择以下两种处理方式:
方式1:将toggleCreateForm设为可选属性
如果组件存在不需要该函数的使用场景,可修改接口标记属性为可选,同时修正返回值类型(原接口定义的boolean有误,setShowCreateWorkoutForm无返回值):
interface FormProps{ toggleCreateForm?: () => void }
使用时需做安全判断,避免运行时错误:
// 示例:点击事件中使用 onClick={() => toggleCreateForm?.()}
方式2:在路由渲染时传递toggleCreateForm
如果组件必须依赖该函数,需调整组件层级或状态管理方式,让路由所在的层级能获取到toggleCreateForm,再传递给组件:
- 比如将路由嵌套在定义
toggleCreateForm的组件内部(如WorkoutList) - 或者通过Context、Redux等状态管理方案共享该函数
- 也可通过React Router的
state传参(不推荐用于函数传递,仅作临时方案)
内容的提问来源于stack exchange,提问作者CamTom47

