这可能是一个奇怪的问题,但我很好奇是否有可能创建一个需要其中一个属性的接口。

所以,例如……

interface Message {
    text: string;
    attachment: Attachment;
    timestamp?: number;
    // ...etc
}

interface Attachment {...}

在上面的情况下,我想确保文本或附件都存在。


我现在就是这么做的。我觉得它有点啰嗦(为slack输入botkit)。

interface Message {
    type?: string;
    channel?: string;
    user?: string;
    text?: string;
    attachments?: Slack.Attachment[];
    ts?: string;
    team?: string;
    event?: string;
    match?: [string, {index: number}, {input: string}];
}

interface AttachmentMessageNoContext extends Message {
    channel: string;
    attachments: Slack.Attachment[];
}

interface TextMessageNoContext extends Message {
    channel: string;
    text: string;
}

当前回答

您可以为所需的条件创建一些接口,并将它们以如下类型连接起来:

interface SolidPart {
    name: string;
    surname: string;
    action: 'add' | 'edit' | 'delete';
    id?: number;
}
interface WithId {
    action: 'edit' | 'delete';
    id: number;
}
interface WithoutId {
    action: 'add';
    id?: number;
}

export type Entity = SolidPart & (WithId | WithoutId);

const item: Entity = { // valid
    name: 'John',
    surname: 'Doe',
    action: 'add'
}
const item: Entity = { // not valid, id required for action === 'edit'
    name: 'John',
    surname: 'Doe',
    action: 'edit'
}

其他回答

谢谢@ryan-cavanaugh,让我找到了正确的方向。

我有一个类似的情况,但是是数组类型。在语法上有点困难,所以我把它放在这里供以后参考:

interface BaseRule {
  optionalProp?: number
}

interface RuleA extends BaseRule {
  requiredPropA: string
}

interface RuleB extends BaseRule {
  requiredPropB: string
}

type SpecialRules = Array<RuleA | RuleB>

// or

type SpecialRules = (RuleA | RuleB)[]

// or (in the strict linted project I'm in):

type SpecialRule = RuleA | RuleB
type SpecialRules = SpecialRule[]

更新:

请注意,稍后在代码中使用声明的变量时,可能仍然会收到警告。然后可以使用(变量作为类型)语法。 例子:

const myRules: SpecialRules = [
  {
    optionalProp: 123,
    requiredPropA: 'This object is of type RuleA'
  },
  {
    requiredPropB: 'This object is of type RuleB'
  }
]

myRules.map((rule) => {
  if ((rule as RuleA).requiredPropA) {
    // do stuff
  } else {
    // do other stuff
  }
})

你可以使用联合类型来做到这一点:

interface MessageBasics {
  timestamp?: number;
  /* more general properties here */
}
interface MessageWithText extends MessageBasics {
  text: string;
}
interface MessageWithAttachment extends MessageBasics {
  attachment: Attachment;
}
type Message = MessageWithText | MessageWithAttachment;

如果你想同时允许文本和附件,你可以这样写

type Message = MessageWithText | MessageWithAttachment | (MessageWithText & MessageWithAttachment);

简单的“二选一”的例子:

type Props =
  | { factor: Factor; ratings?: never }
  | { ratings: Rating[]; factor?: never }

我在为我的案例寻找答案时偶然发现了这个线索(要么是propA,要么是prob,要么都不是)。Ryan Fujiwara的回答几乎做到了,但我已经丢失了一些支票。

我的解决方案:

interface Base {   
  baseProp: string; 
}

interface ComponentWithPropA extends Base {
  propA: string;
  propB?: never;
}

interface ComponentWithPropB extends Base {
  propB: string;
  propA?: never;
} 

interface ComponentWithoutProps extends Base {
  propA?: never;
  propB?: never;
}

type ComponentProps = ComponentWithPropA | ComponentWithPropB | ComponentWithoutProps;

这个解决方案使所有检查保持原样。也许有人会发现这很有用:)

你可以更深入地使用@robstarbuck解决方案创建以下类型:

type Only<T, U> = {
  [P in keyof T]: T[P];
} & {
  [P in keyof U]?: never;
};

type Either<T, U> = Only<T, U> | Only<U, T>;

然后消息类型看起来像这样

interface MessageBasics {
  timestamp?: number;
  /* more general properties here */
}
interface MessageWithText extends MessageBasics {
  text: string;
}
interface MessageWithAttachment extends MessageBasics {
  attachment: string;
}
type Message = Either<MessageWithText, MessageWithAttachment>;

使用此解决方案,您可以轻松地在MessageWithText或MessageWithAttachment类型中添加更多字段,而无需在其他类型中排除它。