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!

2

There are 2 best solutions below

0
On

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!

0
On

Just use | never instead of ?:

...
type Parse = [(keyof Formats), string | never];
type Format = [keyof Formats, (Date | number) | never];
type Convert = [keyof Formats, keyof Formats, string | never];
...