Machen Sie alle Eigenschaften innerhalb einer Typescript-Schnittstelle optional

89

Ich habe eine Schnittstelle in meiner Anwendung:

interface Asset {
  id: string;
  internal_id: string;
  usage: number;
}

das ist Teil einer Post-Schnittstelle:

interface Post {
  asset: Asset;
}

Ich habe auch eine Schnittstelle für einen Post-Entwurf, bei der das Asset-Objekt möglicherweise nur teilweise erstellt wird

interface PostDraft {
  asset: Asset;
}

Ich möchte zulassen, dass ein PostDraftObjekt ein Teil-Asset-Objekt hat, während die Typen der vorhandenen Eigenschaften überprüft werden (daher möchte ich es nicht einfach austauschen any).

Grundsätzlich möchte ich einen Weg finden, um Folgendes zu generieren:

interface AssetDraft {
  id?: string;
  internal_id?: string;
  usage?: number;
}

ohne die AssetSchnittstelle komplett neu zu definieren . Gibt es eine Möglichkeit, dies zu tun? Wenn nicht, wie würde ich meine Typen in dieser Situation klug anordnen?

jkjustjoshing
quelle
Heute müssen Sie manuell , dass die zweite Schnittstelle machen, obwohl dies in naher Zukunft ändern könnte: Zeigt das aus Teil Arten Problem auf der ts Wenn Sie daran interessiert Repo.
Alex Guerra

Antworten:

196

Dies ist in TypeScript <2.1 nicht möglich, ohne eine zusätzliche Schnittstelle mit optionalen Eigenschaften zu erstellen. Dies ist jedoch möglich, indem zugeordnete Typen in TypeScript 2.1+ verwendet werden.

Verwenden Sie dazu den Partial<T>Typ, den TypeScript standardmäßig bereitstellt.

interface PostDraft {
    asset: Partial<Asset>;
}

Jetzt sind alle Eigenschaften assetaktiviert, sodass Sie Folgendes tun können:

const postDraft: PostDraft = {
    asset: {
        id: "some-id"
    }
};

Über Partial<T>

Partial<T>wird definiert als ein zugeordnetes Typ , das jede Eigenschaft in der vorgesehenen Art optional (unter Verwendung des macht ?Token).

type Partial<T> = {
    [P in keyof T]?: T[P];
};

Lesen Sie hier und im Handbuch mehr über zugeordnete Typen .

David Sherret
quelle
@ David ist es möglich, den anderen Weg zu gehen? Felder, die zuvor teilweise waren, als erforderlich markieren? Und ist es auch möglich, dies pro Feld zu tun?
Tony
1
@ Tony ja, es wird mit bedingten Typen in TS 2.8 möglich sein (siehe Required<T>).
David Sherret
2
omg das ist so eine saubere lösung. Ich wünschte, ich wüsste das etwas früher
CiriousJoker
3
Ich war immer wieder erstaunt über den Gedanken, der an TS ging ... von Microsoft nicht weniger. Wünschte, die gleiche Absicht ging in ihre Webbrowser-Entwicklung (-_-)
GreaterKing
4

Die Eigenschaften in der Schnittstelle sind entweder optional oder nicht. Sie können dieselbe Schnittstelle nicht einmal als optional und einmal als erforderlich verwenden.

Was Sie tun können, ist eine Schnittstelle mit optionalen Eigenschaften für die AssetDraftund dann eine Klasse mit obligatorischen Eigenschaften für Asset:

interface AssetDraft {
    id?: string;
    internal_id?: string;
    usage?: number;
}

class Asset {
    static DEFAULT_ID = "id";
    static DEFAULT_INTERNAL_ID = "internalid";
    static DEFAULT_USAGE = 0;

    id: string;
    internal_id: string;
    usage: number;

    constructor(draft: AssetDraft) {
        this.id = draft.id || Asset.DEFAULT_ID;
        this.internal_id = draft.internal_id || Asset.DEFAULT_INTERNAL_ID;
        this.usage = draft.usage || Asset.DEFAULT_USAGE;
    }
}

Die Standardwerte hier sind statische Elemente, aber Sie können diese auf andere Weise abrufen oder einen Fehler auslösen, falls sie fehlen.

Ich finde diesen Weg sehr bequem, wenn ich mit jsons arbeite, die vom Server empfangen werden (oder etwas Ähnliches), die Schnittstellen die json-Daten darstellen und die Klassen die tatsächlichen Modelle sind, die unter Verwendung der jsons als Anfangswerte erstellt werden.

Nitzan Tomer
quelle
Ja, Sie können ... wie in der Antwort von David Sherret angegeben ... Sie müssten eine generische Typvariable verwenden, um Ihre Schnittstelle als Teil <T> ..T zu übergeben, das Ihre Schnittstelle ist.
GreaterKing
@greaterKing jetzt ist es möglich und einfach
Nitzan Tomer
0

Zusätzlich zu David Sherret antworte nur ein paar Zeilen von meiner Seite, wie es direkt ohne Partial<T>Typ implementiert werden kann, um das Thema besser zu verstehen.

interface IAsset {
  id: string;
  internal_id: string;
  usage: number;
}

interface IPost {
  asset: IAsset;
}

interface IPostDraft {
  asset: { [K in keyof IAsset]?: IAsset[K] };
}

const postDraft: IPostDraft = {
  asset: {
    usage: 123
  }
};
WebBrother
quelle
0

Wenn ich eine explizite AssetDraftSchnittstelle haben möchte, würde ich eine Kombination aus extendsund verwenden Partial:

interface Asset {
  id: string;
  internal_id: string;
  usage: number;
}

interface AssetDraft extends Partial<Asset> {}
Dhilt
quelle
-1

Wie wäre es mit einem gewaltsamen Werfen eines leeren Objekts, z

const draft = <PostDraft>{}
draft.id = 123
draft.internal_id = 456
draft.usage = 789

Wenn Sie dies wirklich wirklich benötigen, können Sie jederzeit eine d.ts-Schnittstelle aus einer Vorlage generieren, die sowohl die optionalen als auch die typisierten Eigenschaften enthält.

Wie Nitzan betonte, sind entweder die Eigenschaften der Typescript-Schnittstelle optional oder nicht

user3893988
quelle