typescript - Gönderilen bir nesneye dayanarak bir nesne türü türetmek mümkün mü?
Tümünde ortak bir alan seti bulunan çeşitli belgelerim var.
Ortak alanları dolduracak ve benzersiz alanları girdi olarak alacak, böylece eksiksiz bir belge oluşturacak bir fonksiyon yaratmaya çalışıyorum.
Bunu çalıştırabiliyorum, ama şu anda nihai türü manuel olarak belirtmem gerekiyor. Typescript'te, verilen alanlara göre nihai türü çıkarmanın bir yolu olup olmadığını merak ediyorum (veya verilen alanlar bilinen hiçbir belge türüyle eşleşmezse bir hata fırlatmak).
İşte şu anda bunun için sahip olduğum kod:
type DocA = {
id: string,
createdAt: string,
onlyDocA: string
}
type DocB = {
id: string,
createdAt: string,
onlyDocB: string
}
type AllDocTypes = DocA | DocB
export const createBaseDocument = <DocType extends AllDocTypes>(
fields: Omit<DocType, 'id' | 'createdAt'>,
): DocType => {
const createdAt = new Date();
return {
id: generateId(),
createdAt: createdAt.toString(),
...fields,
} as DocType
}
function generateId(): string {
return 'id'
}
// testA should be of type `DocA`
const testA = createBaseDocument({ id: 'a', createdAt: '123', onlyDocA: 'hello'})
// testA should be of type `DocB`
const testB = createBaseDocument({ id: 'a', createdAt: '123', onlyDocB: 'hello'})
// testC should throw a type error
const testC = createBaseDocument({ id: 'a', createdAt: '123', onlyDocC: 'hello'})
// testD should throw a type error
const testD = createBaseDocument({ id: 'a', createdAt: '123', onlyDocA: 'hello', onlyDocB: 'hello'})
typescript oyun alanı
Cevap #1
Fonksiyona ne girdiğini daha iyi kontrol etmek istiyorsanız, bunun yerine fonksiyon aşırı yüklemesini kullanmak isteyebilirsiniz:
type DocA = {
id: string,
createdAt: string,
onlyDocA: string
}
type DocB = {
id: string,
createdAt: string,
onlyDocB: string
}
function createBaseDocument(fields: Omit<DocA, 'id'| 'createdAt'>): DocA;
function createBaseDocument(fields: Omit<DocB, 'id'| 'createdAt'>): DocB;
function createBaseDocument(fields: Omit<DocA, 'id'| 'createdAt'>|Omit<DocB, 'id'| 'createdAt'>): DocB|DocA{
const createdAt = new Date();
return {
id:generateId(),
createdAt: createdAt.toString(),
...fields
}
}
Bu şekilde createBaseDocument({onlyDocA:'a'}) ve createBaseDocument({onlyDocB:'b'}) sorun olmayacak, ancak createBaseDocument({onlyDocA:'a',onlyDocB:'b'}) bir hataya yol açacaktır
Cevap #2
Lazar'ın cevabını nedenine dair bir açıklama ile genişletmek için:
Yaklaşımınız başarısız oluyor çünkü Omit<AllDocTypes, "id"|"createdAt"> beklediğiniz şeyi yapmıyor. Sadece onlyDocA veya onlyDocB özelliklerini bırakmak istiyorsunuz, ama işler böyle yürümüyor. Aslında elde ettiğiniz şey boş bir nesne {}:
type testOmit = Omit<AllDocTypes, "id"|"createdAt">; // {}
{} tipi aslında bu bağlamda çok geniş bir tiptir ve "herhangi bir tipte herhangi sayıda özelliğe sahip bir nesne" anlamına gelir. Bu yüzden fonksiyon çağrısında istediğinizi belirtebilirsiniz.
Bunun neden olduğuna gelince, bunun sebebi onu bir birleşim tipiyle birlikte generik bir parametre türü olarak kullanmanızdır. Derleyici, hangi somut türün bir argüman olarak yerine konulduğunu bilmez, bu nedenle Omit yardımcısı DocA | DocB birleşimi ile çalışır.
Muhtemelen bildiğiniz gibi, birleşik nesneler üzerinde yalnızca paylaşılan özelliklere erişilebilir (aksi güvenli olmazdı), bu nedenle, "id" ve "createdAt" paylaşılan özellikleri kaldırdıktan sonra, geriye boş bir nesne kalır (onlyDocA ve onlyDocB paylaşılmamaktadır).
DocBase ile jenerik kesiştirmenin (<T extends Onlys> (fields: T): DocBase & T) fazla özellik eklenmesini neden engellemediğine gelince, bunun nedeni açık bir parametre açıklaması olmadığı sürece, T'nin geçirilen argümandan çıkarılmasıdır, örneğin:
/**
function createBaseDocument<{
onlyDocA: string;
whatever: string;
}>(fields: {
onlyDocA: string;
whatever: string;
}): DocBase & {
onlyDocA: string;
whatever: string;
}*/
const z = createBaseDocument({ onlyDocA: "a", whatever: "whenever" }); // ok
Çözümün işe yaraması için açık olmalısınız:
const x = createBaseDocument<OnlyDocA>({ onlyDocA: 'a' }); //ok
const y = createBaseDocument<OnlyDocA>({ onlyDocA: 'a', onlyDocB: 'b' ,onlyDocX: 'x' }); //error
Oyun alanı
