Сделать все свойства в интерфейсе Typescript необязательным
У меня есть интерфейс в моем приложении:
interface Asset {
id: string;
internal_id: string;
usage: number;
}
который является частью почтового интерфейса:
interface Post {
asset: Asset;
}
У меня также есть интерфейс, который предназначен для черновика сообщения, где объект ресурса может быть только частично сконструирован
interface PostDraft {
asset: Asset;
}
Я хочу разрешить объекту PostDraft
иметь объект частичного объекта, все еще проверяя типы свойств, которые есть (поэтому я не хочу просто заменять его any
).
В основном я хочу, чтобы можно было создать следующее:
interface AssetDraft {
id?: string;
internal_id?: string;
usage?: number;
}
без полного переопределения интерфейса Asset
. Есть ли способ сделать это? Если нет, то каким будет разумный способ упорядочить мои типы в этой ситуации?
Ответы
Ответ 1
Это невозможно в TypeScript <2.1 без создания дополнительного интерфейса с дополнительными свойствами; однако это возможно при использовании Mapped Types в TypeScript 2. 1+.
Для этого используйте тип Partial<T>
который TypeScript предоставляет по умолчанию.
interface PostDraft {
asset: Partial<Asset>;
}
Теперь все свойства asset
являются необязательными, что позволит вам сделать следующее:
const postDraft: PostDraft = {
asset: {
id: "some-id"
}
};
О Partial<T>
Partial<T>
определяется как сопоставленный тип, который делает каждое свойство необязательным (с помощью маркера ?
).
type Partial<T> = {
[P in keyof T]?: T[P];
};
Подробнее о отображаемых типах читайте здесь и в справочнике.
Ответ 2
Свойства в интерфейсе являются необязательными: нет, вы не можете использовать один и тот же интерфейс один раз как необязательный и один раз, как необходимо.
Что вы можете сделать, так это иметь интерфейс с необязательными свойствами для AssetDraft
, а затем класс с обязательными свойствами для 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;
}
}
Значения по умолчанию здесь являются статическими членами, но вы можете получить их другими способами или выбросить ошибку, если они отсутствуют.
Я нахожу этот способ очень удобным при работе с jsons, которые получены с сервера (или что-то подобное), интерфейсы представляют данные json, а классы - это фактические модели, которые построены с использованием jsons в качестве начальных значений.
Ответ 3
Как насчет принудительного бросания пустого объекта, например
const draft = <PostDraft>{}
draft.id = 123
draft.internal_id = 456
draft.usage = 789
Если вам это действительно нужно, вы всегда можете сгенерировать интерфейс d.ts из шаблона, который задает как необязательные, так и типизированные свойства.
Как отметил Ницан, либо свойства интерфейса Typescript являются необязательными, либо нет
Ответ 4
В дополнение к Дэвиду Шеррету ответьте лишь на несколько строк с моей стороны, как это можно реализовать напрямую без Partial<T>
для лучшего понимания предмета.
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
}
};