React 和 Typescript 接口“OR”运算符

Fel*_*tos 9 interface typescript reactjs

我开始使用 React 和 Typescript 开发一个应用程序。顺便说一句,我是这两件事的新手。

我有一个反应组件和一些接口,我希望该接口中的每个字段都是必需的,但如果实现了一个特定字段,则不再需要其他字段。

在这种情况下,如果我设置“对象”,我希望不需要实现其他 2 个字段。

interface ITextFieldProps {
    label: string,
    required: boolean,
    object?: any
}

export default class TextField extends React.Component<ITextFieldProps, any> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

希望有人帮忙,加油!

编辑

首先我要感谢所有的答案,我真的从他们每个人身上学到了很多东西。他们帮助我了解我的问题并找到最佳解决方案。

我将传递的对象是元数据,因此它仅在 http 调用后在运行时可用。话虽这么说,无论如何我都无法在开发时检查并输入它。

所以我使用反应道具传播解决了我的问题。

(今天打字稿还不支持,但总有一天会的嘿嘿https://github.com/Microsoft/TypeScript/issues/2103):

export interface IMetadata {
    label: string,
    required: boolean
}

interface TextFieldProps extends IMetadata {
    ...
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以传递一个带有 props spread 的对象,然后当我调用 label="" 时,它会自动覆盖 {...this.metadata.nome} 中的标签:

<TextField {...this.metadata.nome} label="OverwriteLabel" />
Run Code Online (Sandbox Code Playgroud)

编辑2

正如 Daniel 所说:不幸的是,JSX 中的类型检查有一个错误,请参阅:

https://github.com/Microsoft/TypeScript/issues/10171

他们计划在 2.1 版本中修复它。在此之前,该技术将不适用于 TSX。(修复后,我会回来更新这个答案。)

Dan*_*ker 9

这是在 TypeScript 中使用联合类型完成的。

interface TextFieldPropsA {
    label: string,
    required: boolean
}

interface TextFieldPropsB {
    object: any
}
Run Code Online (Sandbox Code Playgroud)

以下是定义它们的并集的方法:

type TextFieldProps = TextFieldPropsA | TextFieldPropsB;
Run Code Online (Sandbox Code Playgroud)

This literally means it must be the first or the second type (it's exactly what you asked for in your question! We're "or"-ing two interfaces).

So:

// no good
const test1: TextFieldProps = { }

// no good, still missing the label
const test2: TextFieldProps = {
    required: true
} 

// this is fine
const test1: TextFieldProps = {
    object: {}
}
Run Code Online (Sandbox Code Playgroud)

(By the way, note that the I prefix is not widely used in TypeScript, because classes and function types are all interfaces so it really doesn't tell you anything useful.)

UPDATE However, you require this to define React props, so you can restrict what is allowed when using JSX syntax. Unfortunately the type checking in JSX has a bug, see:

https://github.com/Microsoft/TypeScript/issues/10171

They plan to fix it for 2.1. Until then, this technique won't work for TSX. (When it's fixed I'll come back and update this answer!)


Nit*_*mer 2

你不能这样做,没有办法在界面中表达如果满足一个属性,则不需要另一个属性。

你有两个选择(据我所知):

(1) 将所有属性声明为可选:

interface ITextFieldProps {
    label?: string,
    required?: boolean,
    object?: any
}
Run Code Online (Sandbox Code Playgroud)

然后检查该值是否有效:

function isValid(props: ITextFieldProps): boolean {
    return props.label || props.required != null || props.object;
}
Run Code Online (Sandbox Code Playgroud)

(2) 定义几个扩展基础接口的接口:

interface ITextFieldPropsBase {}

interface ITextFieldPropsLabel extends ITextFieldPropsBase {
    label: string;
}

interface ITextFieldPropsBool extends ITextFieldPropsBase {
    value: boolean;
}

interface ITextFieldPropsAny extends ITextFieldPropsBase {
    object: any;
}

class TextField<T extends ITextFieldPropsBase> extends React.Component<T, any> { ... }
Run Code Online (Sandbox Code Playgroud)

这样你每次只能获得这些属性之一。


编辑

@DanielEarwicker 在我的第二个示例中评论了如何将 props 传递到组件中。
使用时jsx

ReactDOM.render(<TextField label="hey" />, document.getElementById("container"));
Run Code Online (Sandbox Code Playgroud)

无法找到它的错误label,但可以使用以下方法完成ReactDOM.render

ReactDOM.render(React.createElement(TextField, { label: "hey" }), document.getElementById("container"));
Run Code Online (Sandbox Code Playgroud)

第二次编辑

看来我的第二个建议对于这个具体情况来说并不是一个好的建议。
我从一个稍微不同的场景中得出了它:

interface BaseProps {
    name: string;
}

abstract class BaseComponent<T extends BaseProps> extends React.Component<T, {}> {
    render() {
        return <div className={ this.props.name }>{ this.getContent() }</div>
    }

    protected abstract getContent(): JSX.Element;
}

interface DateProps extends BaseProps {
    date: Date;
}

class DateComponent extends BaseComponent<DateProps> {
    protected getContent(): JSX.Element {
        return <div>{ DateComponent.formatDate(this.props.date) }</div>
    }

    private static formatDate(date: Date): string {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

由于我实际上为每个接口都有不同的组件类,因此我不需要执行任何类型保护、转换或任何其他技巧。

@DanielEarwicker 的答案在 OP 描述的场景中是正确的。

  • 通过联合类型,这是完全可能的 - 请参阅我的答案。 (2认同)