Möglicherweise eine seltsame Frage, aber ich bin gespannt, ob es möglich ist, eine Schnittstelle zu erstellen, bei der die eine oder andere Eigenschaft erforderlich ist.
Also zum Beispiel ...
interface Message {
text: string;
attachment: Attachment;
timestamp?: number;
// ...etc
}
interface Attachment {...}
Im obigen Fall möchte ich sicherstellen, dass entweder text
oder attachment
vorhanden ist.
Hoffentlich macht das Sinn.
Danke im Voraus!
Bearbeiten: So mache ich es gerade. Dachte, es war ein bisschen ausführlich (Eingabe von Botkit für 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;
}
typescript
Dsifford
quelle
quelle
Antworten:
Sie können dazu einen Unionstyp verwenden:
interface MessageBasics { timestamp?: number; /* more general properties here */ } interface MessageWithText extends MessageBasics { text: string; } interface MessageWithAttachment extends MessageBasics { attachment: Attachment; } type Message = MessageWithText | MessageWithAttachment;
Wenn Sie sowohl Text als auch Anhang zulassen möchten, würden Sie schreiben
quelle
Message
akzeptiert{text: 'a', attachment: 'b'}
typescriptlang.org/play/...Wenn Sie wirklich nach "der einen oder der anderen Eigenschaft" suchen und nicht nach beiden, können Sie
never
im Erweiterungstyp Folgendes verwenden: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'}
Beispiel auf dem Spielplatz
quelle
Danke @ ryan-cavanaugh, der mich in die richtige Richtung gebracht hat.
Ich habe einen ähnlichen Fall, aber dann mit Array-Typen. Ich habe ein bisschen mit der Syntax zu kämpfen, deshalb habe ich sie hier zur späteren Bezugnahme eingefügt:
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[]
Aktualisieren:
Beachten Sie, dass Sie später möglicherweise immer noch Warnungen erhalten, wenn Sie die deklarierte Variable in Ihrem Code verwenden. Sie können dann die
(variable as type)
Syntax verwenden. Beispiel: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 } })
quelle
.map()
Codes gibt. Verwenden Sie beimin
Bediener den Typschutz. TypeScript führt die richtige Typinferenz usw. durch. Ihr Zustand wäre alsoif('requiredPropA' in rule) { /* inside here, TS knows rule is of type <RuleA> */ }
Sie können einige Schnittstellen für die erforderlichen Bedingungen erstellen und sie in einem Typ wie hier verbinden:
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' }
quelle
Ok, also nach einigem Ausprobieren und Googeln stellte ich fest, dass die Antwort für meinen Anwendungsfall nicht wie erwartet funktionierte. Für den Fall, dass jemand anderes das gleiche Problem hat, dachte ich, ich würde mitteilen, wie ich es zum Laufen gebracht habe. Meine Schnittstelle war so:
export interface MainProps { prop1?: string; prop2?: string; prop3: string; }
Was ich suchte, war eine Typdefinition, die besagen würde, dass wir weder prop1 noch prop2 definieren könnten. Wir hätten prop1 definieren können, aber nicht prop2. Und schließlich habe prop2 definiert, aber nicht prop1. Hier ist, was ich als Lösung gefunden habe.
interface MainBase { prop3: string; } interface MainWithProp1 { prop1: string; } interface MainWithProp2 { prop2: string; } export type MainProps = MainBase | (MainBase & MainWithProp1) | (MainBase & MainWithProp2);
Dies funktionierte perfekt, mit der Ausnahme, dass beim Versuch, entweder auf prop1 oder prop2 in einer anderen Datei zu verweisen, immer wieder eine Eigenschaft abgerufen wurde, kein TS-Fehler vorhanden ist. So konnte ich das umgehen:
import {MainProps} from 'location/MainProps'; const namedFunction = (props: MainProps) => { if('prop1' in props){ doSomethingWith(props.prop1); } else if ('prop2' in props){ doSomethingWith(props.prop2); } else { // neither prop1 nor prop2 are defined } }
Ich dachte nur, ich würde das teilen, denn wenn ich auf dieses bisschen Verrücktheit stoßen würde, wäre es wahrscheinlich auch jemand anderes.
quelle
Es gibt einige coole Typescript-Optionen, die Sie unter https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk verwenden können
Ihre Frage lautet: Erstellen Sie eine Schnittstelle, in der entweder "Text" oder ein Anhang vorhanden ist. Sie könnten so etwas tun wie:
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 = { }
quelle
text
XOR zuattachment
verwendenOmit
?