TypeScript类型兼容问题求助:Recipe接口与API数据适配困惑
问题分析与解决方案
核心问题是你混淆了API返回的原始数据类型和业务代码中处理后的数据类型,同时对TypeScript的数组/元组类型理解有误。
误区拆解
你最初的Recipe接口里,extendedIngredients用了元组类型([{ original: string; }]),这表示它是一个固定长度为1的数组,但Spoonacular API返回的是长度不固定的对象数组,所以会触发类型不匹配错误。
当你改成extendedIngredients: string[]后,TypeScript会认为info.extendedIngredients是字符串数组,每个元素都是string,自然没有original属性,因此报错'original' does not exist on type 'string'。
正确解决步骤
1. 定义匹配API返回的原始类型
首先明确API返回的extendedIngredients是对象数组,每个对象包含original字段,先定义准确的原始数据接口:
// 单个配料的原始类型 interface RecipeIngredient { original: string; // 若需要其他API返回的字段(如id、name),可在此添加 } // API返回的完整Recipe原始类型 interface RawRecipe { id: number; title: string; image: string; servings: number; readyInMinutes: number; dairyFree: boolean; glutenFree: boolean; ketogenic: boolean; vegan: boolean; vegetarian: boolean; extendedIngredients: RecipeIngredient[]; // 这里是可变长度的对象数组,不是元组 }
2. 定义业务处理后的类型(可选)
如果希望业务代码中使用精简后的配料数组(仅存字符串),可单独定义业务类型:
interface ProcessedRecipe { id: number; title: string; image: string; servings: number; readyInMinutes: number; dairyFree: boolean; glutenFree: boolean; ketogenic: boolean; vegan: boolean; vegetarian: boolean; extendedIngredients: string[]; // 处理后的字符串数组 }
3. 调整请求与处理代码
区分原始数据和处理后数据,避免类型混淆:
useEffect(() => { const getRecipe = async () => { try { const res = await fetch( `https://api.spoonacular.com/recipes/${id}/information?apiKey=KEY&includeNutrition=false` ); const rawData: RawRecipe = await res.json(); // 指定原始数据类型 recipeInfo(rawData); } catch (err) { console.error(err); } }; getRecipe(); }, [id]); const recipeInfo = (info: RawRecipe) => { // 转换为业务处理后的类型 const recipeData: ProcessedRecipe = { id: info.id, title: info.title, image: info.image, servings: info.servings, readyInMinutes: info.readyInMinutes, dairyFree: info.dairyFree, glutenFree: info.glutenFree, ketogenic: info.ketogenic, vegan: info.vegan, vegetarian: info.vegetarian, extendedIngredients: info.extendedIngredients.map(ing => ing.original), // 此时ing是RecipeIngredient类型,存在original属性 }; console.log(recipeData); };
简化方案(无需区分双类型)
如果不需要保留原始配料对象,可直接在获取数据时转换并匹配业务接口:
interface Recipe { id: number; title: string; image: string; servings: number; readyInMinutes: number; dairyFree: boolean; glutenFree: boolean; ketogenic: boolean; vegan: boolean; vegetarian: boolean; extendedIngredients: string[]; } useEffect(() => { const getRecipe = async () => { try { const res = await fetch( `https://api.spoonacular.com/recipes/${id}/information?apiKey=KEY&includeNutrition=false` ); const rawData = await res.json(); // 转换配料后赋值给Recipe类型 const recipeData: Recipe = { ...rawData, extendedIngredients: rawData.extendedIngredients.map((ing: RecipeIngredient) => ing.original) }; console.log(recipeData); } catch (err) { console.error(err); } }; getRecipe(); }, [id]);
关键知识点总结
- 元组vs数组:
[{original: string}]是固定长度的元组,API返回的可变长度数组应使用RecipeIngredient[]。 - 类型分离:API原始数据类型和业务处理后类型建议分开定义,避免类型混淆。
- 类型安全:尽量避免使用
any,通过定义明确的接口保证类型检查有效性。
内容的提问来源于stack exchange,提问作者Neelam
相关产品推荐
相关产品推荐

