admin管理员组文章数量:1122846
I've set up a wrapper component for the Shopify Resource Picker.
import { useAppBridge } from '@shopify/app-bridge-react';
import { Button, type ButtonProps } from '@shopify/polaris';
import { useCallback } from 'react';
export type ResourcePickerSelectPayload = NonNullable<
Awaited<ReturnType<typeof shopify.resourcePicker>>
>;
interface ProductResourcePickerProps {
onSelect: (resources: ResourcePickerSelectPayload) => void;
options?: Parameters<typeof shopify.resourcePicker>[0];
buttonLabel?: string;
buttonProps?: Omit<ButtonProps, 'onClick'>;
}
export function ResourcePicker({
onSelect,
options = { type: 'product' },
buttonLabel = 'Select products',
buttonProps = {},
}: ProductResourcePickerProps) {
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>
);
}
The return value of shopify.ResourcePicker
is as the type:
type ResourcePickerApi = (options: ResourcePickerOptions) => Promise<SelectPayload<ResourcePickerOptions['type']> | undefined>;
which is causing my ResourcePickerSelectPayload
to be:
type ResourcePickerSelectPayload = ResourceSelection<"product" | "variant" | "collection">[] & {
selection: ResourceSelection<"product" | "variant" | "collection">[];
}
What I am struggling with is how to have the value from options.type
(product, variant or collection) be passed through so that my component returns the narrowed type. i.e. if options.type === 'product'
then selected
would be the ResourceSelection<'product'>
.
The ResourceSelection
type is not exported, so I don't know a way to access it besides what I've got.
I think part of the issue is that my ResourcePickerSelectPayload
is not a generic, but ResourceSelection
is.
I tried to pass the value of options.type
into my function and do some conditional returns but it didn't work.
I've set up a wrapper component for the Shopify Resource Picker.
import { useAppBridge } from '@shopify/app-bridge-react';
import { Button, type ButtonProps } from '@shopify/polaris';
import { useCallback } from 'react';
export type ResourcePickerSelectPayload = NonNullable<
Awaited<ReturnType<typeof shopify.resourcePicker>>
>;
interface ProductResourcePickerProps {
onSelect: (resources: ResourcePickerSelectPayload) => void;
options?: Parameters<typeof shopify.resourcePicker>[0];
buttonLabel?: string;
buttonProps?: Omit<ButtonProps, 'onClick'>;
}
export function ResourcePicker({
onSelect,
options = { type: 'product' },
buttonLabel = 'Select products',
buttonProps = {},
}: ProductResourcePickerProps) {
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>
);
}
The return value of shopify.ResourcePicker
is as the type:
type ResourcePickerApi = (options: ResourcePickerOptions) => Promise<SelectPayload<ResourcePickerOptions['type']> | undefined>;
which is causing my ResourcePickerSelectPayload
to be:
type ResourcePickerSelectPayload = ResourceSelection<"product" | "variant" | "collection">[] & {
selection: ResourceSelection<"product" | "variant" | "collection">[];
}
What I am struggling with is how to have the value from options.type
(product, variant or collection) be passed through so that my component returns the narrowed type. i.e. if options.type === 'product'
then selected
would be the ResourceSelection<'product'>
.
The ResourceSelection
type is not exported, so I don't know a way to access it besides what I've got.
I think part of the issue is that my ResourcePickerSelectPayload
is not a generic, but ResourceSelection
is.
I tried to pass the value of options.type
into my function and do some conditional returns but it didn't work.
1 Answer
Reset to default 0Generics come in very handy here. They basically allow you to dynamically infer and enforce types based on the input.
With generics, we could rewrite the code to look like this;
interface ProductResourcePickerProps<
T extends "product" | "variant" | "collection"
> {
onSelect: (resources: ResourceSelection<T>[]) => void;
options?: { type: T } & Omit<ResourcePickerOptions, "type">;
buttonLabel?: string;
buttonProps?: Omit<ButtonProps, "onClick">;
}
export function ResourcePicker<T extends "product" | "variant" | "collection">({
onSelect,
options = { type: "product" } as { type: T },
buttonLabel = "Select resources",
buttonProps = {},
}: ProductResourcePickerProps<T>) {
const shopify = useAppBridge();
const handleOpenPicker = useCallback(async () => {
const selected = await shopify.resourcePicker(options);
if (selected?.selection) {
onSelect(selected.selection);
}
}, [onSelect, options, shopify]);
return (
<Button onClick={handleOpenPicker} {...buttonProps}>
{buttonLabel}
</Button>
);
}
So once you call ResourcePicker
, onSelect
already knows what came in. Something like this;
<ResourcePicker
onSelect={(selected) => {
// TypeScript shows ResourceSelection<'product'>[] as the type already
console.log(selected);
}}
options={{ type: 'product' }}
buttonLabel="Select products"
/>
For what it's worth, I checked the resource picker documentation and I think there's more to do in this wrapper, but focusing on the context of the code in the OP, this explains the use of generics to solve your problem.
本文标签:
版权声明:本文标题:typescript - How to narrow types returned by custom component that uses a 3rd party functiontypes - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736305648a1932664.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论