Search code examples
typescriptlodash

Lodash _.reduce() "Expected 2-3 arguments, but got 2" error in TypeScript


I am trying to reduce ListAdItemResponse[] collection to {[name: string]: Date} type in one of my tests and check that each subsequent published date comes before the current one (e.g. sorted by publishedAt in descending order):

 _.reduce<ListAdItemResponse, {[name: string]: Date}>(
   data,
   (result, val) => {
     expect(new Date(val.publishedAt)).to.be.beforeTime(
       new Date(result.publishedAt),
      )
     return {publishedAt: val.publishedAt}
   })

Using TypeScript 2.9.2, I am getting this strange contradictory error ("Expected 2-3 arguments, but got 2"). According to Lodash docs, the third argument is optional, but I can't get past this error no matter what I try. Any idea what may be wrong here? Thanks.

P.S. This compiles without error, but breaks my test, obviously:

_.reduce<ListAdItemResponse, {[name: string]: Date}>(
  data,
  (result, val) => {
    expect(new Date(val.publishedAt)).to.be.beforeTime(
      new Date(result.publishedAt),
    )
    return {publishedAt: val.publishedAt}
  }, {}) // with {} added as 3rd argument

Update 9/2/18:

Per @Matt's suggestion, I've tried this approach, but got 3 compilation errors this time. Looks like it's expecting alis.data collection type to be {[name: string]: Date} instead of ListAdItemResponse[] as it is now:

_.reduce<{[name: string]: Date}>(
  alis.data, // <-- error 1: Argument of type 'ListAdItemResponse[]' is not assignable to parameter of type '{ [name: string]: Date; } | null | undefined'.Type 'ListAdItemResponse[]' is not assignable to type '{ [name: string]: Date; }'. Index signature is missing in type 'ListAdItemResponse[]'.
  (result, val) => { // <-- errors 2,3: Parameter 'result' implicitly has an 'any' type. Parameter 'val' implicitly has an 'any' type.
    expect(new Date(val.publishedAt)).to.be.beforeTime(
      new Date(result.publishedAt),
    )
    return {publishedAt: val.publishedAt}
  })

Solution

  • If you want to omit the third argument, then the type of the input items and the accumulator should be the same, so you are expected to pass only a single type argument:

    _.reduce<{[name: string]: Date}>(...)
    

    I filed an issue for the contradictory error message.

    Round 2

    Even though the number of errors has increased, you are closer to the goal! Assuming the definition of ListAdItemResponse looks something like this:

    interface ListAdItemResponse {
        publishedAt: Date;
        // other fields...
    }
    

    then your call to _.reduce does not match the overload you intend because ListAdItemResponse is not assignable to {[name: string]: Date} because it doesn't have an index signature. (Confusingly, TypeScript's error #1 is based on the incorrect assumption that you want a different overload that is meant for reducing over keys of an object.) There's a special exception for object literal types like the one you are returning from the reducer. What you should probably do is change the state type to {publishedAt: Date} because that's the only field you're using:

    _.reduce<{publishedAt: Date}>(...)
    

    This should make errors #2 and #3 go away too.