这可能是一个奇怪的问题,但我很好奇是否有可能创建一个需要其中一个属性的接口。
所以,例如……
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;
}
我进一步压缩了@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>
之后,可以在没有任何中间接口/类型的情况下定义它:
type Message {
type?: string;
channel?: string;
user?: string;
// ...etc
} & Either<{message: string}, {attachment: Attachment}>
下面是一个非常简单的实用程序类型,我使用它来创建一个新类型,允许多个接口/类型中的一个被称为InterfacesUnion:
export type InterfacesUnion<Interfaces> = BuildUniqueInterfaces<UnionToIntersection<Interfaces>, Interfaces>;
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
export type BuildUniqueInterfaces<CompleteInterface, Interfaces> = Interfaces extends object
? AssignNever<CompleteInterface, Interfaces>
: never;
type AssignNever<T, K> = K & {[B in Exclude<keyof T, keyof K>]?: never};
它可以这样使用:
type NewType = InterfacesUnion<AttachmentMessageNoContext | TextMessageNoContext>
它通过接受接口/类型的联合来运行,构建一个包含它们所有属性的单一接口,并返回相同的接口/类型的联合,其中每个接口都包含其他接口拥有而它们没有的附加属性,这些属性被设置为可选的never ([propertyName]?:永远不要)。
有例子/解释的游乐场
你可以更深入地使用@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类型中添加更多字段,而无需在其他类型中排除它。
有一些很酷的Typescript选项,你可以使用https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk
你的问题是:创造一个既存在“文本”又存在附件的界面。你可以这样做:
interface AllMessageProperties {
text: string,
attachement: string,
}
type Message = Omit<AllMessageProperties, 'text'> | Omit<AllMessageProperties, 'attachement'>;
const messageWithText : Message = {
text: 'some text'
}
const messageWithAttachement : Message = {
attachement: 'path-to/attachment'
}
const messageWithTextAndAttachement : Message = {
text: 'some text',
attachement: 'path-to/attachment'
}
// results in Typescript error
const messageWithOutTextOrAttachement : Message = {
}
我进一步压缩了@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>
之后,可以在没有任何中间接口/类型的情况下定义它:
type Message {
type?: string;
channel?: string;
user?: string;
// ...etc
} & Either<{message: string}, {attachment: Attachment}>