typescript - Cara membuat tipe mirip Partial yang mewajibkan satu properti diatur
Kami memiliki sebuah struktur yang seperti berikut:
export type LinkRestSource = {
model: string;
rel?: string;
title?: string;
} | {
model?: string;
rel: string;
title?: string;
} | {
model?: string;
rel?: string;
title: string;
};
Yang hampir sama dengan mengatakan
type LinkRestSource = Partial<{model: string, rel: string, title: string}>
Kecuali bahwa Partial akan memungkinkan sebuah objek kosong untuk dikirimkan sementara tipe awal mengharuskan salah satu properti untuk dikirimkan.
Bagaimana saya bisa membuat generic seperti Partial, tetapi yang berperilaku seperti struktur saya di atas?
Jawaban #1
Anda sedang mencari sesuatu yang mengambil sebuah tipe T dan menghasilkan tipe terkait yang berisi setidaknya satu properti dari T. Ini seperti Partial<T>, tetapi mengecualikan objek kosong.
Jika begitu, ini dia:
type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U]
Untuk membedahnya, pertama-tama:
AtLeastOne<T>adalahPartial<T>yang dipotong dengan sesuatu.U[keyof U]berarti bahwa itu adalah gabungan dari semua nilai properti dariU.- Dan saya telah mendefinisikan nilai default dari
Usebagai tipe yang dipetakan di mana setiap properti dariTdipetakan kePick<T, K>, sebuah tipe dengan satu properti untuk kunciK.
Sebagai contoh, berikut ini setara karena ia "memilih" properti 'foo' dari tipe asli:
Pick<{foo: string, bar: number},'foo'>
// is equivalent to
{foo: string}
yang berarti bahwa U[keyof U] dalam kasus ini adalah gabungan dari semua tipe properti tunggal yang mungkin dari T. Mari kita lihat langkah demi langkah bagaimana ini bekerja pada tipe konkret berikut:
type FullLinkRestSource = {
model: string;
rel: string;
title: string;
}
type LinkRestSource = AtLeastOne<FullLinkRestSource>
yang berkembang menjadi:
type LinkRestSource = AtLeastOne<FullLinkRestSource, {
[K in keyof FullLinkRestSource]: Pick<FullLinkRestSource, K>
}>
atau
type LinkRestSource = AtLeastOne<FullLinkRestSource, {
model: Pick<FullLinkRestSource, 'model'>,
rel: Pick<FullLinkRestSource, 'rel'>,
title: Pick<FullLinkRestSource, 'title'>
}>
atau
type LinkRestSource = AtLeastOne<FullLinkRestSource, {
model: {model: string},
rel: {rel: string},
title: {title: string}>
}>
atau
type LinkRestSource = Partial<FullLinkRestSource> & {
model: {model: string},
rel: {rel: string},
title: {title: string}>
}[keyof {
model: {model: string},
rel: {rel: string},
title: {title: string}>
}]
atau
type LinkRestSource = Partial<FullLinkRestSource> & {
model: {model: string},
rel: {rel: string},
title: {title: string}>
}['model' | 'rel' | 'title']
atau
type LinkRestSource = Partial<FullLinkRestSource> &
({model: string} | {rel: string} | {title: string})
atau
type LinkRestSource = {model?: string, rel?: string, title?: string} &
({model: string} | {rel: string} | {title: string})
atau
type LinkRestSource = { model: string, rel?: string, title?: string }
| {model?: string, rel: string, title?: string}
| {model?: string, rel?: string, title: string}
yang saya kira, itulah yang kamu inginkan.
Anda bisa mencobanya:
const okay0: LinkRestSource = { model: 'a', rel: 'b', title: 'c' }
const okay1: LinkRestSource = { model: 'a', rel: 'b' }
const okay2: LinkRestSource = { model: 'a' }
const okay3: LinkRestSource = { rel: 'b' }
const okay4: LinkRestSource = { title: 'c' }
const error0: LinkRestSource = {} // missing property
const error1: LinkRestSource = { model: 'a', titel: 'c' } // excess property on string literal
Jawaban #2
Ada solusi lain jika Anda tahu properti mana yang Anda inginkan.
type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>
Ini juga akan memungkinkan Anda untuk mengunci beberapa kunci dari satu jenis, misalnya.
type LinkRestSource = AtLeast<T, 'model' | 'rel'>
Jawaban #3
Versi yang lebih sederhana dari solusi ini:
type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]
jadi seluruh implementasinya menjadi
type FullLinkRestSource = {
model: string;
rel: string;
title: string;
}
type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]
type LinkRestSource = AtLeastOne<FullLinkRestSource>
const okay0: LinkRestSource = { model: 'a', rel: 'b', title: 'c' }
const okay1: LinkRestSource = { model: 'a', rel: 'b' }
const okay2: LinkRestSource = { model: 'a' }
const okay3: LinkRestSource = { rel: 'b' }
const okay4: LinkRestSource = { title: 'c' }
const error0: LinkRestSource = {} // missing property
const error1: LinkRestSource = { model: 'a', titel: 'c' } // incorrectly spelled property
dan ini link playground TS untuk mencobanya
