可能是一个奇怪的问题,但我很好奇是否可以创建一个需要一个属性或另一个属性的接口.
所以,例如......
interface Message {
text: string;
attachment: Attachment;
timestamp?: number;
// ...etc
}
interface Attachment {...}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,我想确定是存在text还是attachment存在.
希望这是有道理的.
提前致谢!
编辑:这就是我现在正在做的事情.认为它有点冗长(输入botkit for slack).
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;
}
Run Code Online (Sandbox Code Playgroud)
rob*_*uck 90
如果您真正追求“一个属性或另一个”而不是两者,您可以never在扩展类型中使用:
interface MessageBasics {
timestamp?: number;
/* more general properties here */
}
interface MessageWithText extends MessageBasics {
text: string;
attachment?: never;
}
interface MessageWithAttachment extends MessageBasics {
text?: never;
attachment: string;
}
type Message = MessageWithText | MessageWithAttachment;
// OK
let foo: Message = {attachment: 'a'}
// OK
let bar: Message = {text: 'b'}
// ? ERROR: Type '{ attachment: string; text: string; }' is not assignable to type 'Message'.
let baz: Message = {attachment: 'a', text: 'b'}
Run Code Online (Sandbox Code Playgroud)
Rya*_*ugh 49
您可以使用联合类型执行此操作:
interface MessageBasics {
timestamp?: number;
/* more general properties here */
}
interface MessageWithText extends MessageBasics {
text: string;
}
interface MessageWithAttachment extends MessageBasics {
attachment: Attachment;
}
type Message = MessageWithText | MessageWithAttachment;
Run Code Online (Sandbox Code Playgroud)
如果你想同时允许文本和附件,你会写
type Message = MessageWithText | MessageWithAttachment | (MessageWithText & MessageWithAttachment);
Run Code Online (Sandbox Code Playgroud)
Dar*_*rek 13
我在寻找我的案例的答案时偶然发现了这个线程(propA、propB 或都没有)。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;
Run Code Online (Sandbox Code Playgroud)
该解决方案使所有检查保持应有的状态。也许有人会发现这很有用:)
Sha*_*hah 11
简单的“需要两个之一”示例:
type Props =
| { factor: Factor; ratings?: never }
| { ratings: Rating[]; factor?: never }
Run Code Online (Sandbox Code Playgroud)
Fab*_*lev 11
我进一步压缩了 @Voskanyan David 的解决方案,得到了我个人认为非常简洁的解决方案:
您仍然需要在某处定义一次 Only<> 和 Either<> 一次
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>
Run Code Online (Sandbox Code Playgroud)
之后,就可以在没有任何中间接口/类型的情况下将其定义为一件事:
type Message = {
type?: string;
channel?: string;
user?: string;
// ...etc
} & Either<{message: string}, {attachment: Attachment}>
Run Code Online (Sandbox Code Playgroud)
谢谢@ 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[]
Run Code Online (Sandbox Code Playgroud)
更新:
请注意,稍后,当您在代码中使用声明的变量时,可能仍会收到警告.然后,您可以使用(variable as type)语法.例:
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
}
})
Run Code Online (Sandbox Code Playgroud)
您可以为所需条件创建几个接口,并将它们加入如下类型:
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'
}
Run Code Online (Sandbox Code Playgroud)
您可以更深入地使用 @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>;
Run Code Online (Sandbox Code Playgroud)
然后Message输入看起来像这样
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>;
Run Code Online (Sandbox Code Playgroud)
使用此解决方案,您可以轻松地在MessageWithText或MessageWithAttachment类型中添加更多字段,而无需将其排除在另一个中。
你也可以这样。
abstract class BaseMessage {
timestamp?: number;
/* more general properties here */
constructor(timestamp?: number) {
this.timestamp = timestamp;
/* etc. for other general properties */
}
}
interface IMessageWithText extends BaseMessage {
text: string;
attachment?: never;
}
interface IMessageWithAttachment extends BaseMessage {
text?: never;
attachment: string;
}
type Message = IMessageWithText | IMessageWithAttachment;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
16569 次 |
| 最近记录: |