I have a record of data (object-literal), and I need to calculate the average of several of its properties. For example, consider that each record reflects a sample of dog breeds and their weights. We have one representative for each breed, but two specific breeds may not appear in the data:
// typescript
type dogSample = {
// 5 core breeds that always appear
bulldog: number,
poodle: number,
pug: number,
chihuahua: number,
boxer: number,
// 2 additional that sometimes appear
dalmatian?: number,
rottweiler?: number, // rottweiler could appear only if dalmatian appears, but not vice versa
// other irrelevant properties
foo: string,
bar: boolean,
baz: number
// and potentially hundreds of unrelated properties
}
My goal is to calculate the average of dogs weights. So I target those properties directly with three functions:
const calcMeanCoreFive = (obj: dogSample) =>
(obj.bulldog + obj.poodle + obj.pug + obj.chihuahua + obj.boxer) / 5;
const calcMeanCoreFivePlusDalmatian = (obj: Required<dogSample>) => // using `Required` solves type problem
(obj.bulldog +
obj.poodle +
obj.pug +
obj.chihuahua +
obj.boxer +
obj.dalmatian) /
6;
const calcMeanCoreFivePlusDalmatianPlusRottw = (obj: Required<dogSample>) =>
(obj.bulldog +
obj.poodle +
obj.pug +
obj.chihuahua +
obj.boxer +
obj.dalmatian +
obj.rottweiler) /
7;
Finally I want to add a wrapper around all three versions such that:
const calcMeanDogSample = (obj: dogSample, nBreeds: 5 | 6 | 7) =>
nBreeds === 5
? calcMeanCoreFive(obj)
: nBreeds === 6
? calcMeanCoreFivePlusDalmatian(obj)
// ^
: calcMeanCoreFivePlusDalmatianPlusRottw(obj);
// ^
// argument of type 'dogSample' is not assignable to parameter of type 'Required<dogSample>'
My attempt to work around this
I tried to type calcMeanDogSample()
with Required<dogSample>
:
const calcMeanDogSample2 = (obj: Required<dogSample>, nBreeds: 5 | 6 | 7) =>
nBreeds === 5
? calcMeanCoreFive(obj)
: nBreeds === 6
? calcMeanCoreFivePlusDalmatian(obj)
: calcMeanCoreFivePlusDalmatianPlusRottw(obj);
This solves the error as far as the function's definition. However, calling calcMeanDogSample2()
and passing an object of type dogSample
wouldn't work:
const someDogSample = {
bulldog: 24,
poodle: 33,
pug: 21.3,
chihuahua: 7,
boxer: 24,
dalmatian: 20,
foo: "abcd",
bar: false,
baz: 123,
} as dogSample;
calcMeanDogSample2(someDogSample, 6);
// ^
// Argument of type 'dogSample' is not assignable to parameter of type // 'Required<dogSample>'.
// Types of property 'dalmatian' are incompatible.
// Type 'number | undefined' is not assignable to type 'number'.
// Type 'undefined' is not assignable to type 'number'.ts(2345)
My question
Is there a way to type calcMeanDogSample()
differently and solve this problem?
Reproducible code at TS playground