Typescript Интерфейс - возможно ли выполнить "один или другой" свойства?
Возможно, странный вопрос, но мне любопытно, можно ли создать интерфейс, где требуется одно свойство или другое.
Итак, например...
interface Message {
text: string;
attachment: Attachment;
timestamp?: number;
// ...etc
}
interface Attachment {...}
В приведенном выше случае я хотел бы убедиться, что существует либо text
, либо attachment
.
Надеюсь, это имеет смысл.
Спасибо заранее!
Изменить: Вот как я это делаю прямо сейчас. Думал, что это было немного многословно (набрав ботки для слабины).
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;
}
Ответы
Ответ 1
Для этого можно использовать тип объединения:
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);
Ответ 2
Спасибо @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[]
Update:
Обратите внимание, что в дальнейшем вы все равно можете получать предупреждения при использовании объявленной переменной в своем коде. Затем вы можете использовать синтаксис (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
}
})
Ответ 3
Вы можете создать несколько интерфейсов для требуемых условий и объединить их в виде, подобном следующему:
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'
}
Ответ 4
Есть несколько классных опций 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 = {
}