Eu tenho uma interface em meu aplicativo:
interface Asset {
id: string;
internal_id: string;
usage: number;
}
que faz parte de uma interface de postagem:
interface Post {
asset: Asset;
}
Eu também tenho uma interface que é para um pós-rascunho, onde o objeto de ativo pode ser apenas parcialmente construído
interface PostDraft {
asset: Asset;
}
Quero permitir que um PostDraft
objeto tenha um objeto de ativo parcial enquanto ainda verifico os tipos nas propriedades que estão lá (portanto, não quero apenas trocá-lo por any
).
Eu basicamente quero uma maneira de gerar o seguinte:
interface AssetDraft {
id?: string;
internal_id?: string;
usage?: number;
}
sem redefinir totalmente a Asset
interface. Existe uma maneira de fazer isso? Se não, qual seria a maneira inteligente de organizar meus tipos nessa situação?
typescript
jkjustjoshing
fonte
fonte
Respostas:
Isso não é possível no TypeScript <2.1 sem criar uma interface adicional com propriedades opcionais; no entanto, isso é possível usando tipos mapeados em TypeScript 2.1+.
Para fazer isso, use o
Partial<T>
tipo que o TypeScript fornece por padrão.interface PostDraft { asset: Partial<Asset>; }
Agora, todas as propriedades ativadas
asset
são opcionais, o que permitirá que você faça o seguinte:const postDraft: PostDraft = { asset: { id: "some-id" } };
Sobre
Partial<T>
Partial<T>
é definido como um tipo mapeado que torna cada propriedade no tipo fornecido opcional (usando o?
token).type Partial<T> = { [P in keyof T]?: T[P]; };
Leia mais sobre os tipos mapeados aqui e no manual .
Parcial Profundo
Se você quiser uma implementação parcial que funcione recursivamente em objetos, poderá usar o seguinte tipo no TS 4.1+:
type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; };
fonte
Required<T>
).As propriedades na interface são opcionais ou não, você não pode usar a mesma interface uma vez como opcional e uma vez como deve.
O que você pode fazer é ter uma interface com propriedades opcionais para o
AssetDraft
e, em seguida, uma classe com propriedades obrigatórias paraAsset
: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; } }
Os valores padrão aqui são membros estáticos, mas você pode obtê-los de outras maneiras ou lançar um erro caso eles estejam ausentes.
Acho essa maneira muito confortável ao trabalhar com jsons que são recebidos do servidor (ou algo semelhante), as interfaces representam os dados json e as classes são os modelos reais que são construídos usando jsons como valores iniciais.
fonte
Se eu quiser uma
AssetDraft
interface explícita , usaria uma combinação deextends
ePartial
:interface Asset { id: string; internal_id: string; usage: number; } interface AssetDraft extends Partial<Asset> {}
fonte
Além de David Sherret responder apenas algumas linhas de minha parte como pode ser implementado diretamente sem
Partial<T>
digitar para melhor compreensão do assunto.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 } };
fonte
Que tal forçar o lançamento de um objeto vazio, por exemplo
const draft = <PostDraft>{} draft.id = 123 draft.internal_id = 456 draft.usage = 789
Se você realmente precisa disso, você sempre pode gerar uma interface d.ts a partir de um modelo que torna as propriedades opcionais e digitadas.
Como Nitzan apontou, as propriedades da interface Typescript são opcionais ou não
fonte