10

Trying to write a basic reducer to return an array of (value of) a key from an array of objects. Some of the key can be missing or undefined.

My code:

const data = [{Key: 56}, {Key: undefined}, {}, {Key: 44}]

const keys = data.reduce((prev, curr) => {
  if (curr.Key) {
    return [...prev, curr.Key];
  } else return prev;
}, []);

It works fine as a plain JS but the TS compiler does not like it. Playground Link

How to fix this? mainly what am I doing wrong? I am a TS noob.

Error:

No overload matches this call.  

Overload 1 of 3, '(callbackfn: (previousValue: { Key: number; } | { Key: undefined; } | { Key?: undefined; }, currentValue: { Key: number; } | { Key: undefined; } | { Key?: undefined; }, currentIndex: number, array: ({ Key: number; } | { ...; } | { ...; })[]) => { ...; } | ... 1 more ... | { ...; }, initialValue: { ...; } | ... 1 more ... | { ...; }): { ...; } | ... 1 more ... | { ...; }', gave the following error.  
Argument of type '(prev: never[], curr: { Key: number; } | { Key: undefined; } | { Key?: undefined; }) => number[]' is not assignable to parameter of type '(previousValue: { Key: number; } | { Key: undefined; } | { Key?: undefined; }, currentValue: { Key: number; } | { Key: undefined; } | { Key?: undefined; }, currentIndex: number, array: ({ Key: number; } | { ...; } | { ...; })[]) => { ...; } | ... 1 more ... | { ...; }'.  
Types of parameters 'prev' and 'previousValue' are incompatible.  
Type '{ Key: number; } | { Key: undefined; } | { Key?: undefined; }' is not assignable to type 'never[]'.  
Type '{ Key: number; }' is missing the following properties from type 'never[]': length, pop, push, concat, and 26 more.  
Overload 2 of 3, '(callbackfn: (previousValue: never[], currentValue: { Key: number; } | { Key: undefined; } | { Key?: undefined; }, currentIndex: number, array: ({ Key: number; } | { Key: undefined; } | { Key?: undefined; })[]) => never[], initialValue: never[]): never[]', gave the following error.  
Argument of type '(prev: never[], curr: { Key: number; } | { Key: undefined; } | { Key?: undefined; }) => number[]' is not assignable to parameter of type '(previousValue: never[], currentValue: { Key: number; } | { Key: undefined; } | { Key?: undefined; }, currentIndex: number, array: ({ Key: number; } | { Key: undefined; } | { Key?: undefined; })[]) => never[]'.  
Type 'number[]' is not assignable to type 'never[]'.  
Type 'number' is not assignable to type 'never'.

3 Answers 3

15

Assign the type of the accumulator ([]) as number[] (TS playground):

const keys = data.reduce((prev, curr) => {
  if (curr.Key) {
    return [...prev, curr.Key];
  } else return prev;
}, [] as number[]);
Sign up to request clarification or add additional context in comments.

Comments

4

You need to specify the type of the second argument of reduce function.

Try this

const data = [{key: 56}, {key: undefined}, {}, {key: 44}]

const keys = data.reduce((keys, current) => {
  if (current.key) {
    return [...keys, current.key];
  } else return keys;
}, [] as number[]);

Edit: I rushed the answer before, also to improve the readbility of the code, I suggest you to make some changes, like name the property key instead of Key, and some better naming for the parameters of the reduce function

Comments

0

.reduce() is a generic method and the right way to do it is to pass resulting type into generic definition (.reduce<number[]>()) (TS playground)

const data = [{Key: 56}, {Key: undefined}, {}, {Key: 44}]

const keys = data.reduce<number[]>((prev, curr) => {
  if (curr.Key) {
    return [...prev, curr.Key];
  } else return prev;
}, []);

Generic-based approach is more preferable because it will do type-checking of what is really returned in the callback, and it will warn if type mismatches

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.