0

The goal: Get rid of the typescript error TS2345 where argument is not assignable to parameter

The Problem: Im trying to curry an arrow function by forwarding all the arguments using a rest parameter and tuple as type. However, because I use a optional element and strictNullChecking it throws an error because it passes on an undefined type as possibility whilst it should (and will not use) the parameter at all if argument is not given.

The Code (Its about the lower functions parse, format, convert):

/* eslint-disable @typescript-eslint/ban-ts-ignore  */
import { parse, format } from 'date-fns'
import curry from 'lodash/curry'

type Formats = { [key: string]: string }
type Parse = [(keyof Formats), string?]
type Format = [keyof Formats, (Date | number)?]
type Convert = [keyof Formats, keyof Formats, string?]

// if you need prototype inheritance then convert functions to methods and bind
export class DateFormatter<T extends Formats> {
  formats: T

  constructor (formats: T) {
    this.formats = formats
  }

  parseToDate = (formatType: keyof T, date: string): Date => {
    return parse(date, this.formats[formatType], new Date())
  }

  formatToString = (formatType: keyof T, date: Date | number) => {
    return format(date, this.formats[formatType])
  }

  convertFormat = (from: keyof T, to: keyof T, formattedDate: string): string => {
    return format(parse(formattedDate, this.formats[from], new Date()), this.formats[to])
  }

  // @ts-ignore
  parse = (...args: Parse) => curry(this.parseToDate).apply(this, args)
  // @ts-ignore
  format = (...args: Format) => curry(this.formatToString).apply(this, args)
  // @ts-ignore
  convert = (...args: Convert) => curry(this.convertFormat).apply(this, args)
}

Sandbox https://codesandbox.io/s/summer-fog-sw7qb?fontsize=14&hidenavigation=1&theme=dark

Any tips and help much appreciated!

matronator
  • 465
  • 6
  • 13
  • 1
    Is this specifically a question about lodash? If so, please tag it. If not, please provide a standalone [mcve] that can be dropped into an IDE like [The TypeScript Playground](https://www.typescriptlang.org/play/) or a link to the above code in some IDE with the proper dependencies already configured. Good luck! – jcalz Feb 10 '20 at 20:04
  • No this is not about lodash. Ive updated with codesandbox. thanks. – Michiel de Vos Feb 10 '20 at 20:14
  • 1
    I doubt that you'll be able to convince the compiler to verify type safety here, especially with lodash's `curry()` typings which look like a whole bunch of overloads. Ultimately it probably comes down to how you plan to use the code, but what do you think of something like this? [sandbox](https://codesandbox.io/s/hopeful-moon-hzpev). – jcalz Feb 10 '20 at 21:23
  • awesome, id only replace the lodash type with my own function type! Thanks! – Michiel de Vos Feb 10 '20 at 21:39

1 Answers1

2

Answer by jcalz:

import { parse, format } from "date-fns";
import _ from "lodash";

type Formats = { [key: string]: string };
type Parse = [(keyof Formats), string?];
type Format = [keyof Formats, (Date | number)?];
type Convert = [keyof Formats, keyof Formats, string?];

// if you need prototype inheritance then convert functions to methods and bind
export class DateFormatter<T extends Formats> {
  formats: T;

  constructor(formats: T) {
    this.formats = formats;
  }

  parseToDate = (formatType: keyof T, date: string): Date => {
    return parse(date, this.formats[formatType], new Date());
  };

  formatToString = (formatType: keyof T, date: Date | number) => {
    return format(date, this.formats[formatType]);
  };

  convertFormat = (
    from: keyof T,
    to: keyof T,
    formattedDate: string
  ): string => {
    return format(
      parse(formattedDate, this.formats[from], new Date()),
      this.formats[to]
    );
  };

  parse: {
    (formatType: keyof T, date: string): Date;
    (formatType: keyof T): (date: string) => Date
  } = (...args: any) => _.curry(this.parseToDate).apply(this, args) as any;

  format: {
    (formatType: keyof T, date: Date | number): string;
    (formatType: keyof T):(date: Date | number) => string;
  } = (...args: any) => _.curry(this.formatToString).apply(this, args) as any;

  convert: {
    (from: keyof T, to: keyof T, formattedDate: string): string;
    (from: keyof T, to: keyof T): (formattedDate: string) => string;
  } = (...args: any) => _.curry(this.convertFormat).apply(this, args) as any;
}

Thanks!