Typescript, array.reduce() dönüş değerinin tipini nasıl belirler?
Typescript (v4.0.2), array.reduce() fonksiyonunun dönüş tipi ne olmalı kararını nasıl verir ?
(Not: Ben reduce() fonksiyonunun kendisinden bahsediyorum, reduce() fonksiyonuna geçirilen callback fonksiyondan değil!)
Sormamın nedeni, TS hatalarından kurtulmak için her zaman bir çözüm yolu bulmam ama sorunların neden var olduğunu asla anlamamamdır. Birkaç soru ve çözüm gördüm, ancak hepsi sadece belirli durumlar için bir çözüm önermekte, daha az ya da çok yetersiz açıklama ile (type-safe-array-reduce, type-safe-way-to-reduce-a-larger-object-into-a-new-type, ...).
Özellikle, aşağıdaki örnekte, reducerFunction() INewType[] döndürürken, TS neden reduce() fonksiyonunun IOldType döndürdüğünü düşünüyor?
Örnek :
// -- interfaces (no error here) --
interface IOldType {
[key: string]: any
}
interface INewType {
propA: string
propB: number
propC: boolean
}
// -- data (no error here) --
const initialValue: INewType[] = [];
const oldData: IOldType[] = [
{ id: 'item-01', b: true, c: null },
{ id: 'item-02', b: false, otherProp: null },
{ id: 'item-03', b: false },
{ id: 'item-04' },
{},
];
// -- reducer function (no error here) --
const reducerFunction = ( acc: INewType[], item: IOldType): INewType[] => {
if( !item.b || !item.id ){ return acc; }
const newItem: INewType = {
propA: item.id,
propB: 0,
propC: item.b
};
return [ ...acc, newItem ]; // <-- (FYI: 'as INewType[]' here doesn't make a difference)
};
// -- calling .reduce() --> TS error --
const newData: INewType[] = oldData.reduce( reducerFunction, initialValue ); // <-- ERROR "TS2740: Type 'IOldType' is missing the following properties from type 'INewType[]': length, pop, push, concat, and 16 more."
// --------
Bu, yapılandırmaya bağlı olarak, "TS2740: 'IOldType' türünde, 'INewType[]' türünden length, pop, push, concat ve 16 tanesi daha olmak üzere aşağıdaki özellikler eksik." veya "Başlatıcı türü IOldType, değişken türü INewType[]'e atanabilir değil" hatasını fırlatır (görünüşe göre. Bence temel problem aynı.).
Sevmediğim geçici çözümler
Bunu çalıştırmak için farklı geçici çözümler yapabilirim, ama hepsi sorunu gerçekten çözmüyormuş gibi görünüyor:
1. (Sıkça önerilen) as INewType[] ifadesini ekleyebilirim:
const newData: INewType[] = oldData.reduce( reducerFunction, initialValue ) as INewType[];
Ama bu 'hile yapmak' gibi geliyor. Yanlış bir şey döndürebilirim ve bunun INewType[] olduğunu iddia edebilirim. Hatta şunu yazabilirim:
const newData: INewType[] = [1,2,3].reduce( (a, i) => [1], [] ) as INewType[];
2. Arayüzde ekstra, gereksiz isteğe bağlı bir anahtar tanımlayabilirim. Bunun neden işe yaradığını hiç bilmiyorum:
interface IOldType {
anyKey?: string // <-- I don't need or want this, but it works if I add it
[key: string]: any
}
3. Birikleticinin tip tanımını ve indirgenin dönüş tipini atlayabilirim (veya 'any' olarak tanımlayabilirim), ama sanırım her zaman doğru tipleri tanımlamak, any kullanmaktan daha iyi olmalı (?)
const reducerFunction = ( acc, item: IOldType ) => {
// ...
}