使用 TypeScript 在 React 中输入默认的直通道具
Typing defaulted passthrough props in React with TypeScript
React 组件通常会接受一些 props 并将其传递给 children。如果 child 上的一个或多个属性由于由 child 的 defaultProps
指定而可选,如何定义 type
或 interface
为 parent 正确接受它自己的和它的 child 的道具?
考虑以下示例:
interface ParagraphProps {
body: string,
imgSrc: string,
}
interface SectionProps extends ParagraphProps {
title: string,
}
class Paragraph extends React.Component<ParagraphProps> {
static defaultProps = {
imgSrc: '../images/section-break.jpg',
};
render() {
const { body, imgSrc } = this.props;
return (
<p>{body}</p>
{!!imgSrc && <img src={imgSrc}>}
);
}
}
class Section extends React.Component<SectionProps> {
render() {
const { title, ...rest } = this.props;
return (
<section>
<h1>{title}</h1>
<Paragraph {...rest}>
</section>
);
}
}
现在,声明 <Section title='T' body='B'>
将导致错误:
Property 'imgSrc' is missing in type [...]
如果相反,我们为 Section
定义道具,如下所示:
interface SectionProps {
title: string,
}
type FullSectionProps = Partial<SectionProps & PartialProps>;
然后我们发现现在title
和body
是可选的,这不是我们想要的
在保持DRY的同时,如何指定Section
的props来规定title
和body
是必须的,imgSrc
是可选的?
由于 ParagraphProps
接口对应于 Paragraph
组件,因此保留那些 "aligned" 可能是有意义的。你知道 imgSrc
有一个默认值,所以在界面中将其标记为可选是有意义的,因为 no-one 使用 Paragraph
组件(不仅Section
组件)需要传入 imgSrc
.
interface ParagraphProps {
body: string,
imgSrc?: string,
}
如果不是 Paragraph
组件的所有用户都需要传入 imgSrc
(Section
组件除外),那么它可能更有意义将该默认值移动到 imgSrc
的 Section
组件。
最后,如果你想让它更动态,你可以做类似下面的事情,尽管在这个例子中它可能比必要的更复杂。
// Only needed if not 3.5+ (https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type)
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
interface ParagraphProps {
body: string;
imgSrc: string;
}
type PropsWithDefaults = keyof typeof Paragraph.defaultProps;
type TransformedProps = Partial<Pick<ParagraphProps, PropsWithDefaults>> &
Omit<ParagraphProps, PropsWithDefaults>;
interface SectionProps extends TransformedProps {
title: string;
}
class Section extends React.Component<SectionProps> {
render() {
const { title, ...rest } = this.props;
return <Paragraph {...rest} />;
}
}
class Paragraph extends React.Component<ParagraphProps> {
static defaultProps = {
imgSrc: "../images/section-break.jpg"
};
render() {
return null;
}
}
<Section body="foo" title="baz" />
<Section body="foo" title="baz" imgSrc="override" />
此处,TransformedProps
类型包含来自 ParagraphProps
的所有道具,其中 Paragraph.defaultProps
中的道具通过使用 Partial
变为可选。有关 Pick
和 Omit
如何形成此结构的更多详细信息,请参阅 Advanced Types documentation。
React 组件通常会接受一些 props 并将其传递给 children。如果 child 上的一个或多个属性由于由 child 的 defaultProps
指定而可选,如何定义 type
或 interface
为 parent 正确接受它自己的和它的 child 的道具?
考虑以下示例:
interface ParagraphProps {
body: string,
imgSrc: string,
}
interface SectionProps extends ParagraphProps {
title: string,
}
class Paragraph extends React.Component<ParagraphProps> {
static defaultProps = {
imgSrc: '../images/section-break.jpg',
};
render() {
const { body, imgSrc } = this.props;
return (
<p>{body}</p>
{!!imgSrc && <img src={imgSrc}>}
);
}
}
class Section extends React.Component<SectionProps> {
render() {
const { title, ...rest } = this.props;
return (
<section>
<h1>{title}</h1>
<Paragraph {...rest}>
</section>
);
}
}
现在,声明 <Section title='T' body='B'>
将导致错误:
Property 'imgSrc' is missing in type [...]
如果相反,我们为 Section
定义道具,如下所示:
interface SectionProps {
title: string,
}
type FullSectionProps = Partial<SectionProps & PartialProps>;
然后我们发现现在title
和body
是可选的,这不是我们想要的
在保持DRY的同时,如何指定Section
的props来规定title
和body
是必须的,imgSrc
是可选的?
由于 ParagraphProps
接口对应于 Paragraph
组件,因此保留那些 "aligned" 可能是有意义的。你知道 imgSrc
有一个默认值,所以在界面中将其标记为可选是有意义的,因为 no-one 使用 Paragraph
组件(不仅Section
组件)需要传入 imgSrc
.
interface ParagraphProps {
body: string,
imgSrc?: string,
}
如果不是 Paragraph
组件的所有用户都需要传入 imgSrc
(Section
组件除外),那么它可能更有意义将该默认值移动到 imgSrc
的 Section
组件。
最后,如果你想让它更动态,你可以做类似下面的事情,尽管在这个例子中它可能比必要的更复杂。
// Only needed if not 3.5+ (https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type)
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
interface ParagraphProps {
body: string;
imgSrc: string;
}
type PropsWithDefaults = keyof typeof Paragraph.defaultProps;
type TransformedProps = Partial<Pick<ParagraphProps, PropsWithDefaults>> &
Omit<ParagraphProps, PropsWithDefaults>;
interface SectionProps extends TransformedProps {
title: string;
}
class Section extends React.Component<SectionProps> {
render() {
const { title, ...rest } = this.props;
return <Paragraph {...rest} />;
}
}
class Paragraph extends React.Component<ParagraphProps> {
static defaultProps = {
imgSrc: "../images/section-break.jpg"
};
render() {
return null;
}
}
<Section body="foo" title="baz" />
<Section body="foo" title="baz" imgSrc="override" />
此处,TransformedProps
类型包含来自 ParagraphProps
的所有道具,其中 Paragraph.defaultProps
中的道具通过使用 Partial
变为可选。有关 Pick
和 Omit
如何形成此结构的更多详细信息,请参阅 Advanced Types documentation。