如何收窄调用第三方函数的自定义组件返回类型
解决Shopify Resource Picker组件的类型收窄问题
问题背景
封装Shopify Resource Picker组件时,当前实现的ResourcePickerSelectPayload是所有资源类型的联合类型,无法根据传入的options.type(product/variant/collection)自动收窄返回值类型。比如传入type: 'product'时,希望onSelect的参数类型自动变为对应product的ResourceSelection类型,而非联合类型。且由于ResourceSelection未被Shopify导出,无法直接引用。
解决方案
将组件定义为泛型组件,基于ResourcePickerOptions的type字段做类型约束,利用TypeScript的类型推断自动推导对应返回类型:
1. 泛型化组件Props和类型定义
修改Props为泛型接口,同时调整选择结果类型为泛型,通过ReturnType和Parameters从shopify.resourcePicker的类型中提取对应类型:
import { useAppBridge } from '@shopify/app-bridge-react'; import { Button, type ButtonProps } from '@shopify/polaris'; import { useCallback } from 'react'; // 泛型选择结果类型,根据传入的资源类型自动推导 export type ResourcePickerSelectPayload<ResourceType extends 'product' | 'variant' | 'collection'> = NonNullable< Awaited<ReturnType<typeof shopify.resourcePicker<ResourceType>>> >; // 泛型化Props接口,绑定资源类型与options.type interface ResourcePickerProps<ResourceType extends 'product' | 'variant' | 'collection'> { onSelect: (resources: ResourcePickerSelectPayload<ResourceType>) => void; options?: Parameters<typeof shopify.resourcePicker<ResourceType>>[0]; buttonLabel?: string; buttonProps?: Omit<ButtonProps, 'onClick'>; } // 泛型化组件,默认类型设为product保持原有行为 export function ResourcePicker<ResourceType extends 'product' | 'variant' | 'collection' = 'product'>({ onSelect, options = { type: 'product' as ResourceType }, buttonLabel = 'Select products', buttonProps = {}, }: ResourcePickerProps<ResourceType>) { const shopify = useAppBridge(); const handleOpenPicker = useCallback(async () => { const selected = await shopify.resourcePicker(options); if (selected) { onSelect(selected); } }, [onSelect, options, shopify]); return ( <Button onClick={handleOpenPicker} {...buttonProps}> {buttonLabel} </Button> ); }
2. 使用示例(自动类型收窄)
当传入不同的options.type时,TypeScript会自动推导onSelect的参数类型:
// 选择product时,参数自动匹配product类型结构 <ResourcePicker options={{ type: 'product' }} onSelect={(selected) => { console.log(selected.selection[0].title); // 类型安全,product包含title字段 }} /> // 选择variant时,参数自动切换为variant类型结构 <ResourcePicker options={{ type: 'variant' }} buttonLabel="Select variants" onSelect={(selected) => { console.log(selected.selection[0].price); // 类型安全,variant包含price字段 }} />
核心原理说明
之前的实现中,ResourcePickerSelectPayload是固定的联合类型,没有和options.type建立关联。通过泛型将ResourceType绑定到options.type和onSelect的参数类型,让TypeScript能根据传入的具体类型自动推导收窄后的类型,无需直接引用未导出的ResourceSelection。
内容的提问来源于stack exchange,提问作者NicO
相关产品推荐
相关产品推荐

