TypeScript报错Property 'original_title' does not exist on type 'string'如何解决
问题排查与修复
存在的问题
- 类型定义错误:
IMovieState里的movie字段定义为string[],但实际TMDB返回的是包含original_title等属性的电影对象数组,同时Action的payload类型也错误定义为string - Reducer字段拼写错误:更新state时写错字段名,原state字段是
movie,返回时写为movies,导致数据没有正确存入对应字段 - useSelector使用错误:
useSelector接收的参数是整个Redux根state,不是单个reducer的IMovieState类型 - 接口数据处理错误:TMDB接口返回的电影列表存放在
res.data.results字段,直接传递res.data会拿到包含分页信息的外层对象,不是电影数组 - 数组合并逻辑错误:Reducer中更新数组时未展开payload,会把整个数组作为单个元素存入目标数组
- HTML结构不规范:
li标签没有外层ul/ol包裹,同时缺少遍历必填的key属性
修复后代码
1. 定义公共电影类型
新建types.ts或者直接加在reducer文件中:
// 可根据TMDB返回字段补全其他属性 export interface IMovie { id: number; original_title: string; }
2. 修正Reducer.ts
import { Action } from './actions' import { IMovie } from './types' export interface IMovieState { movie: IMovie[] // 修正为电影对象数组类型 } const initialState: IMovieState = { movie: [], } export const movieReducer = ( state: IMovieState = initialState, action: Action, ) => { switch (action.type) { case 'GET_MOVIES': { // 修正字段名,同时展开payload合并数组 return { ...state, movie: [...state.movie, ...action.payload] } } default: return state } }
3. 修正Actions.ts
import axios from 'axios' import { Dispatch } from 'redux' import { IMovie } from './types' // 修正payload类型为电影对象数组 export type Action = { type: 'GET_MOVIES'; payload: IMovie[] } const getMovies = (movies: IMovie[]): Action => ({ type: 'GET_MOVIES', payload: movies, }) export const loadMovies = () => { return function (dispatch: Dispatch<Action>) { axios .get(`${process.env.REACT_APP_API_KEY}`) .then((res) => { // 取接口返回的results字段,就是电影列表数组 dispatch(getMovies(res.data.results)) }) .catch((err) => console.log(err)) } }
4. 修正Store.ts,导出RootState类型
import { createStore, applyMiddleware } from 'redux' import { composeWithDevTools } from "redux-devtools-extension"; import reduxThunk from 'redux-thunk' import rootReducer from '../redux/rootReducer' const middlewares = [reduxThunk] export const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(...middlewares))) // 导出根state类型,供useSelector使用 export type RootState = ReturnType<typeof store.getState>
5. 修正App.tsx
import React, { useEffect } from 'react' import { useSelector, useDispatch } from 'react-redux' import { loadMovies } from './redux/actions' import { RootState } from './redux/store' import './App.css' function App() { const dispatch = useDispatch() // 修正useSelector类型,注意此处的movieReducer要和你rootReducer里挂载的字段名对应 const movies = useSelector((state: RootState) => state.movieReducer.movie) useEffect(() => { dispatch(loadMovies()) }, [dispatch]) return <div className="App"> <ul> {movies.map(mov => ( <li key={mov.id}>{mov.original_title}</li> ))} </ul> </div> } export default App
注意:如果你的
rootReducer中给movieReducer挂载的别名不是movieReducer,需要把state.movieReducer.movie里的第一个movieReducer替换为你实际定义的别名。
内容的提问来源于stack exchange,提问作者Faruk Suljagic
相关产品推荐
相关产品推荐

