3
votes

I have the current code running on typescriptlang.org (typescript playground)

I have read up on the importance of types in typeScript and how to use them in functions. However i'm struggling with adding the following types in this reduce method:

// Types for car
type Car = {
 name:string,
 age:number,
 registered: boolean
};

// Reduce function to hopefully get the total number of cars that are registered.

function totalRegisteredCars(cars:Car[]) {
 cars.reduce((acc:number , car:Car) => {
    if(car.registered === true ) {
        acc + 1;
    }
 },0);
}

var cars = [
{name:'vw' , age: 30, registered: true},
{name:'vw' , age: 32, registered: true},
{name:'Merc' , age: 25, registered: false},
{name:'bmw' , age: 20, registered: false},
{name:'bmw' , age: 21, registered: true},
{name: 'ford', age: 31, registered: false},
{name: 'pinto', age: 43, registered: false},
{name: 'uno', age: 41, registered: true},
{name: 'ford', age: 30, registered: true},
{name: 'Mustang', age: 19, registered: false}
];

console.log(totalRegisteredCars(cars));

When having this in the https://www.typescriptlang.org/play i get the following error:


Error message

No overload matches this call. Overload 1 of 3, '(callbackfn: (previousValue: Car, currentValue: Car, currentIndex: number, array: Car[]) => Car, initialValue: Car): Car', gave the following error.

Argument of type '(acc: number, car: Car) => void' is not assignable to parameter of type '(previousValue: Car, currentValue: Car, currentIndex: number, array: Car[]) => Car'. Types of parameters 'acc' and 'previousValue' are incompatible. Type 'Car' is not assignable to type 'number'.

Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: Car, currentIndex: number, array: Car[]) => number, initialValue: number): number', gave the following error. Argument of type '(acc: number, car: Car) => void' is not assignable to parameter of type '(previousValue: number, currentValue: Car, currentIndex: number, array: Car[]) => number'. Type 'void' is not assignable to type 'number'.


Question

I could be missing a simple step, but the compiler complains about how my accumulator which i have typed as a number and car which i have typed as Car seems to give me the error logged above.

I just want to know why I cannot set a type to my accumulator as a number. and how should I set types inside a reduce function in future?

1
Maybe it's because the initial value of the reduce is 0 which is not of type car? (genuine suggestion, not an answer)evolutionxbox
Ya, i was thinking if i made the initial value to a number then it would accept the type "number". Will look into this, thanks so much.tony2tones
I think my suggestion is incorrect as the reduce already has a type of number for the accumulatorevolutionxbox

1 Answers

3
votes

The reason you get the error is because your reduce callback isn't implemented correctly. It's supposed to return a result of the reduce operation after going through every element of the array.

function totalRegisteredCars(cars: Car[]) {
 cars.reduce((acc:number , car:Car) => {
    let newReducedValue = acc;
    if(car.registered === true ) {
        newReducedValue = acc + 1;
    }
    return newReducedValue;
 }, 0);
}

Of course, there is no need to write so many lines, but I wanted to make it obvious where the problem lies. Here's an alternative way to write the same thing:

cars.reduce((acc: number, car: Car) => acc + car.registered ? 1 : 0, 0);
// or even use car.registered by itself, JS will cast it to a number,
// I just prefer to make it more obvious

Another thing I've noticed with your code is that the type signature of totalRegisteredCars is actually (cars: Car[]) => void, because there is no return statement in the function itself. We reduce the array of Cars to a single number, but then we do nothing with that number.

You probably meant to return a number from your totalRegisteredCars function (and then log it). A good way to remind yourself and the compiler about this is to add an explicit return type like so:

function totalRegisteredCars(cars: Car[]): number {

As soon as you do so, you will notice you're getting a new error:

A function whose declared type is neither 'void' nor 'any' must return a value. (2355)

At which point we can say "Thanks, Typescript" and fix out code to do what we intended while pondering on how helpful types are.

function totalRegisteredCars(cars: Car[]): number {
  return cars.reduce((acc, car) => acc + car.registered ? 1 : 0, 0);
}

Note that I haven't used type declarations inside the lambda function at all in my latest example. There reason is that the type of car is inferred from type of cars, and the type of acc is inferred from the second argument to the reduce function: 0.

As a final note, I'd like to add that all of the above is under the assumption that you're just playing with the language. If you really intend to count the number of cars that are registered in your array of cars, a potentially simpler way would be

cars.filter(car => car.registered).length