Svelte组件返回值类型转换与泛型适配技术咨询
解决Svelte+Typesense组件的TypeScript联合类型检查错误
我有一个对接Typesense的Svelte组件,负责将搜索结果返回给父组件,通过插槽实现差异化展示。使用联合类型标注结果返回类型时,出现了以下TypeScript错误:
Property 'price' does not exist on type 'Fruit | Fluid'. Property 'price' does not exist on type 'Fluid'.ts(2339) Property 'viscosity' does not exist on type 'Fruit | Fluid'. Property 'viscosity' does not exist on type 'Fruit'.ts(2339) Property 'price' does not exist on type 'Highlight<Fruit | Fluid>'. Property 'price' does not exist on type 'Highlight<Fluid>'.ts(2339) Property 'viscosity' does not exist on type 'Highlight<Fruit | Fluid>'. Property 'viscosity' does not exist on type 'Highlight<Fruit>'.ts(2339)
当前仅存在类型检查错误,运行时无异常。我想知道是否可以通过转换组件返回的result变量类型,或者为组件传入指定类型,让它仅返回ExampleResponse<Fruit>或ExampleResponse<Fluid>这类单一类型(实际项目中的类型不止这两种)。
示例代码
main +page.svelte
<script lang="ts"> import Search from './Search.svelte'; </script> <Search searchParams="fruit" let:result> <div> <div>Fruit is {result.document.name}</div> <div>Price is {result.document.price}</div> <div>Highlighted price is {result.highlight.price.snippet}</div> </div> </Search> <Search searchParams="fluid" let:result> <div> <div>Fluid is {result.document.name}</div> <div>Viscosity is {result.document.viscosity}</div> <div>Highlighted viscosity is {result.highlight.viscosity.snippet}</div> </div> </Search>
Search.svelte
<script lang="ts"> import { onMount } from 'svelte'; export let searchParams: 'fruit' | 'fluid'; let results: ExampleResponse<Fruit | Fluid>[] = []; onMount(() => search); async function search() { // 根据searchParams填充fruit或fluid数据 } </script> {#each results as result} <slot {result} /> {/each}
ambient.d.ts
type Fruit = { id: string; name: string; color: string; price: number; }; type Fluid = { id: string; name: string; color: string; viscosity: number; }; type Highlight<T> = { [K in keyof T]: HighlightInfo; }; type HighlightInfo = { marked: string; snippet: string; }; type ExampleResponse<T> = { document: T; highlight: Highlight<T>; };
我曾尝试在if块中使用in进行类型收窄,虽然有效但只是临时解决方案:
<Search searchParams="fruit" let:result> {#if 'price' in result.document && 'price' in result.highlight} <div> <div>Fruit is {result.document.name}</div> <div>Price is {result.document.price}</div> <div>Highlighted price is {result.highlight.price.snippet}</div> </div> {/if} </Search>
解决方案:为Search组件添加泛型约束
通过给Search组件定义类型映射和泛型,可以让组件根据searchParams的值自动推导返回的ExampleResponse<T>类型,彻底解决类型检查错误。
修改Search.svelte
<script lang="ts"> import { onMount } from 'svelte'; // 定义searchParams值与对应文档类型的映射关系 type SearchTypeMap = { fruit: Fruit; fluid: Fluid; // 后续新增类型直接在此添加键值对即可 }; // 约束searchParams为映射类型的键 export let searchParams: keyof SearchTypeMap; // 根据searchParams的类型自动推导results的类型 let results: ExampleResponse<SearchTypeMap[typeof searchParams]>[] = []; onMount(() => search); async function search() { // 实际请求中可根据searchParams获取对应类型的数据 // 这里用类型断言保证返回值类型正确 const response = await fetch(`/api/search?type=${searchParams}`); results = await response.json() as typeof results; } </script> {#each results as result} <slot {result} /> {/each}
父组件使用效果
修改后,父组件中使用Search时,TypeScript会自动根据searchParams的值推导result的具体类型,无需额外的类型收窄代码,直接访问专属属性即可通过类型检查:
<Search searchParams="fruit" let:result> <div> <div>Fruit is {result.document.name}</div> <div>Price is {result.document.price}</div> <div>Highlighted price is {result.highlight.price.snippet}</div> </div> </Search> <Search searchParams="fluid" let:result> <div> <div>Fluid is {result.document.name}</div> <div>Viscosity is {result.document.viscosity}</div> <div>Highlighted viscosity is {result.highlight.viscosity.snippet}</div> </div> </Search>
扩展说明
- 新增类型时,只需在
SearchTypeMap中添加新的键值对(如vegetable: Vegetable),无需修改其他逻辑 - 如果需要更灵活的泛型传入,也可以让组件直接接收泛型参数,但类型映射的方式能更好地保证
searchParams与返回类型的一致性,避免参数错误
内容的提问来源于stack exchange,提问作者benjababe
相关产品推荐
相关产品推荐

