打字稿React组件具有功能超载和通用参数
我有一个名为ResourceElect的组件,它基本上是一个普通选择组件上的包装器,但是使用Resources
(诸如{ID,name,title})的动态格式,而不是options
(用{label,value})修复了格式,以渲染选择项目。它在JavaScript环境中起作用,但我发现自己无法为其实现良好的打字条定义。目前,我能做的就是使用函数过载和通用参数来实现这样的直接用法:
export type ValueableType = number | string;
export type DefaultResourceType = Record<string, string | number>;
export interface BaseResourceSelectProps<
RT extends DefaultResourceType = DefaultResourceType,
VT extends ValueableType | number[] | string[] = number
> {
/** resource list used to render options */
resources?: RT[];
/** the default key to render label */
labelKey?: keyof RT;
// In my real implementation it's from extends AntdSelectProps<VT>
// But I think value and onChange can represent this part
value?: VT;
onChange?: (value: VT) => void;
}
export interface GenericResourceSelectProps extends BaseResourceSelectProps {
mode?: 'multiple';
valueKey?: string;
}
export function ResourceSelect<
RT extends DefaultResourceType = DefaultResourceType,
VK extends keyof RT = 'id'
>(
props: {
valueKey?: VK;
mode?: never;
} & BaseResourceSelectProps<RT, RT[VK]>
): JSX.Element;
export function ResourceSelect<
RT extends DefaultResourceType = DefaultResourceType,
VK extends keyof RT = 'id'
>(
props: {
valueKey?: VK;
mode: 'multiple';
} & BaseResourceSelectProps<RT, RT[VK] extends string ? string[] : number[]>
): JSX.Element;
/**
* ResourceSelect
*/
export function ResourceSelect(genericProps: unknown) {
const props = genericProps as
// some implementation
}
// Storybook cases
export const CodeBoard: Story<{
value: number | string | number[] | string[];
onSelect: (v: unknown) => void;
}> = ({ value, onSelect }) => {
return (
<div style={{ maxWidth: 200 }}>
<h3>Normal usage</h3>
<ResourceSelect
resources={resources}
value={value as number}
defaultValue={1}
onChange={(num: number) => onSelect(num)}
style={{ width: '100%' }}
/>
<h3>Custome valueKey</h3>
<ResourceSelect
resources={resources}
value={value as string}
valueKey="strId"
defaultValue="id 1"
onChange={(str: string) => onSelect(str)}
style={{ width: '100%' }}
/>
<h3>Multiple mode</h3>
<ResourceSelect
resources={resources}
value={value as number[]}
defaultValue={[1]}
mode="multiple"
onChange={(numArr: number[]) => onSelect(numArr)}
style={{ width: '100%' }}
/>
</div>
);
};
DesignBoard.args = {
value: undefined,
};
DesignBoard.argTypes = { onSelect: { action: 'onSelect' } };
我现在的问题是我无法重新安装该组件。这样的代码只是不起作用:
type ResourceSelectProps = Parameters<typeof ResourceSelect>[0];
interface Location {
id: number;
address: string;
}
/**
* LocationSelect
*/
export const LocationSelect: FC<ResourceSelectProps<Location>> = (props) => {
// Call an API hook that fetches locations
const { data = [], isValidating } = useLocations();
return (
<ResourceSelect
resources={data}
placeholder="Location"
loading={isValidating}
labelKey="address"
dropdownMatchSelectWidth={false}
{...props}
/>
);
};
I have a component named ResourceSelect, it's basically a wrapper over a normal Select component, but uses resources
(dynamic format like {id, name, title}) instead of options
(fixed format with {label, value}) to render the select items. It worked in JavaScript environment, but I found myself cannot implement a good typescript definition for it. Currently I can do is using function overload and generic params to achieve directly usage like this:
export type ValueableType = number | string;
export type DefaultResourceType = Record<string, string | number>;
export interface BaseResourceSelectProps<
RT extends DefaultResourceType = DefaultResourceType,
VT extends ValueableType | number[] | string[] = number
> {
/** resource list used to render options */
resources?: RT[];
/** the default key to render label */
labelKey?: keyof RT;
// In my real implementation it's from extends AntdSelectProps<VT>
// But I think value and onChange can represent this part
value?: VT;
onChange?: (value: VT) => void;
}
export interface GenericResourceSelectProps extends BaseResourceSelectProps {
mode?: 'multiple';
valueKey?: string;
}
export function ResourceSelect<
RT extends DefaultResourceType = DefaultResourceType,
VK extends keyof RT = 'id'
>(
props: {
valueKey?: VK;
mode?: never;
} & BaseResourceSelectProps<RT, RT[VK]>
): JSX.Element;
export function ResourceSelect<
RT extends DefaultResourceType = DefaultResourceType,
VK extends keyof RT = 'id'
>(
props: {
valueKey?: VK;
mode: 'multiple';
} & BaseResourceSelectProps<RT, RT[VK] extends string ? string[] : number[]>
): JSX.Element;
/**
* ResourceSelect
*/
export function ResourceSelect(genericProps: unknown) {
const props = genericProps as
// some implementation
}
// Storybook cases
export const CodeBoard: Story<{
value: number | string | number[] | string[];
onSelect: (v: unknown) => void;
}> = ({ value, onSelect }) => {
return (
<div style={{ maxWidth: 200 }}>
<h3>Normal usage</h3>
<ResourceSelect
resources={resources}
value={value as number}
defaultValue={1}
onChange={(num: number) => onSelect(num)}
style={{ width: '100%' }}
/>
<h3>Custome valueKey</h3>
<ResourceSelect
resources={resources}
value={value as string}
valueKey="strId"
defaultValue="id 1"
onChange={(str: string) => onSelect(str)}
style={{ width: '100%' }}
/>
<h3>Multiple mode</h3>
<ResourceSelect
resources={resources}
value={value as number[]}
defaultValue={[1]}
mode="multiple"
onChange={(numArr: number[]) => onSelect(numArr)}
style={{ width: '100%' }}
/>
</div>
);
};
DesignBoard.args = {
value: undefined,
};
DesignBoard.argTypes = { onSelect: { action: 'onSelect' } };
My problem now is that I cannot re-encapsulate this component. Code like this just not working:
type ResourceSelectProps = Parameters<typeof ResourceSelect>[0];
interface Location {
id: number;
address: string;
}
/**
* LocationSelect
*/
export const LocationSelect: FC<ResourceSelectProps<Location>> = (props) => {
// Call an API hook that fetches locations
const { data = [], isValidating } = useLocations();
return (
<ResourceSelect
resources={data}
placeholder="Location"
loading={isValidating}
labelKey="address"
dropdownMatchSelectWidth={false}
{...props}
/>
);
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我自己想出了自己的想法,但仍然无法在封装的组件中添加默认值:
像这样的代码仍然无法正常工作:
I kind of figured it out myself, but still not be able to add default values in the encapsulated component:
Code like this still not working: