TypeScript eksik alanları olması gereken bir tipe ekle


Veritabanı kodum için bir yardımcı fonksiyon oluşturmaya çalışıyorum. Veritabanındaki her tablonun bazı ortak alanları vardır (pk, oluşturulma tarihi vb.) ve bu alanlar, diğer tabloların arayüzlerinin genişlettiği ortak bir arayüz olarak uygulanır. Bu ortak özellikleri otomatik olarak bir nesneye ekleyen ve yeni nesneyi döndüren bir fonksiyon yapmak istiyorum, ancak TypeScript, anlayamadığım bir şekilde alt tür atamasıyla ilgili hata veriyor...

Type '{ id: string; } & Pick<T, Exclude<keyof T, "id">>' is not assignable to type 'T'.
  '{ id: string; } & Pick<T, Exclude<keyof T, "id">>' is assignable to the constraint of type 
'T', but 'T' could be instantiated with a different subtype of constraint 'Common'

Bunu çoğaltmak için TS playground üzerinde minimal bir kod örneği oluşturdum. Common temel arayüzünü genişleten T tipi için, Common'da olanlar dışında T'nin tüm gerekli alanlarını içeren bir nesneyi almak ve ardından eklenen alanlarla birlikte nesneyi döndürmek istiyorum.

interface Common {
    id: string;
}

interface Table extends Common {
    data: string;
}

function makeDbEntry<T extends Common>(initial: Omit<T, 'id'>): T {
    const common: Common = {
        id: 'random string',
    };
    const ret: T = {
        ...common,
        ...initial,
    };
    return ret;
}

Oyun Alanı Bağlantısı

Gördüğüm kadarıyla, T'den alanları açıkça listelemeden (örneğin 'T eksi Common' yapamıyoruz) alanları kaldırmanın bir yolu görünmüyor...

Cevap #1

Koşullu tipler, eğer tür parametreleri içeriyorsa tamamen çözülemez. Bu, en azından bu kullanım durumu için, Omit<T, 'id'> temel olarak opaktır, TS bu tipin içinin ne olduğunu göremez, bu sadece T'den farklı bir tiptir.

Spread işlemini yaptığınızda, TS bunun ortak ile Omit<T, 'id'> arasında bir kesişim (intersection) oluşturacağını doğru şekilde anlar ({ id: string; } & Omit<T, 'id'>). Ama Omit dediğim gibi opak olduğundan, bu türden daha fazla bir sadeleştirme yapılamaz. İnsan için bu T tipine kolayca çözülebilir olsa da, typescript bu türü nasıl sadeleştireceğini bilmez.

Eğer T'yi somut bir tip ile değiştirirseniz, ts örneğin { id: string; } & Omit<Table, 'id'>'nin Table ile aynı olduğunu kolayca anlayabilir, bu yüzden bu çalışır:

function makeDbEntryForTable(initial: Omit<Table, 'id'>): Table {
    const common: Common = {
        id: 'random string',
    };
    const ret: Table = {
        ...common,
        ...initial,
    };
    return ret;
}

Genel sürüm için, çoğu durumda geliştiricinin derleyiciden daha iyi anladığı durumlarda olduğu gibi, en iyi seçeneğiniz bir tür onayı kullanmaktır:

function makeDbEntry<T extends Common>(initial: Omit<T, 'id'>): T {
    const common: Common = {
        id: 'random string',
    };
    const ret: T = {
        ...common,
        ...initial,
    } as T;
    return ret;
}

Oyun Alanı Bağlantısı

İlgili makaleler