jeudi 18 août 2022

Make a class iterable from a property using [Symbol.iterator] as generator function

I'm trying to make Month class iterable from a property in the constructor. So I used this[Symbol.iterable] as a generator function inside a constructor scope to be able to use the for...of loop and get access to the sequence of days. I'm following this tutorial, but it uses Vanilla JavaScript. The problem is happening in the iterator typing, I don't know what to do. If it works in pure JavaScript, I think it should work in TypeScript using the right typing.

//class Month

class Month {
  lang: string;
  name: string;
  number: number;
  year: number;
  numberOfDays: number;
  [Symbol.iterator]: () => Generator<Day, void, undefined>; // here is the problem

  constructor(date = null, lang = 'default') {
    const day = new Day(null, lang);
    const monthsSize = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    this.lang = lang;

    this.name = day.month;
    this.number = day.monthNumber;
    this.year = day.year;
    this.numberOfDays = monthsSize[this.number - 1];

    if (this.number === 2) {
      this.numberOfDays += isLeapYear(day.year) ? 1 : 0;
    }

    this[Symbol.iterator] = function* () {
      let number = 1;
      yield this.getDay(number);
      while (number < this.numberOfDays) {
        ++number;
        yield this.getDay(number);
      }
    };
  }

  getDay(day: number) {
    return new Day(new Date(this.year, this.number - 1, day), this.lang);
  }
}

const month = new Month();

for(day of month) {
  console.log(day.Date)
}

//other code needed

const getWeekNumber = (day: Date) => {
  const firstDayOfTheYear = new Date(day.getFullYear(), 0, 1);
  const pastDaysOfYear =
    (day.getTime() - firstDayOfTheYear.getTime()) / (24 * 60 * 60 * 1000);

  return Math.ceil((pastDaysOfYear + firstDayOfTheYear.getDay() + 1) / 7);
};

const isLeapYear = (year: number) =>
  year % 100 === 0 ? year % 400 === 0 : year % 4 === 0;

class Day {
  Date: Date;
  dayOfMonth: number;
  dayNumberOfWeek: number;
  dayNameOfWeek: string;
  dayNameOfWeekShort: string;
  year: number;
  yearShort: number;
  month: string;
  monthNumber: number;
  monthShort: string;
  timestamp: number;
  week: number;

  constructor(day: Date | null = null, lang = 'default') {
    day = day ?? new Date();

    this.Date = day;
    this.dayOfMonth = day.getDate();
    this.dayNumberOfWeek = day.getDay() + 1;
    this.dayNameOfWeek = day.toLocaleString(lang, {
      weekday: 'long',
    });
    this.dayNameOfWeekShort = day.toLocaleString(lang, {
      weekday: 'short',
    });
    this.year = day.getFullYear();
    this.yearShort = Number(day.toLocaleString(lang, { year: '2-digit' }));
    this.month = day.toLocaleString(lang, { month: 'long' });
    this.monthNumber = day.getMonth() + 1;
    this.monthShort = day.toLocaleString(lang, { month: 'short' });
    this.timestamp = day.getTime();
    this.week = getWeekNumber(day);
  }

  get isToday() {
    return this.isEqualTo(new Date());
  }

  isEqualTo(day: Day | Date) {
    day = day instanceof Day ? day.Date : day;

    return (
      day.getDate() === this.dayOfMonth &&
      day.getMonth() === this.monthNumber - 1 &&
      day.getFullYear() === this.year
    );
  }

  format(dateStr: string) {
    return dateStr
      .replace(/\bYYYY\b/, this.year.toString())
      .replace(/\b(YYY|YY)\b/, this.yearShort.toString())
      .replace(/\bWWW\b/, this.week.toString().padStart(2, '0'))
      .replace(/\bW\b/, this.week.toString())
      .replace(/\bMMMM*\b/, this.month)
      .replace(/\bMMMM\b/, this.month)
      .replace(/\bMMM\b/, this.monthShort)
      .replace(/\bMM\b/, this.monthNumber.toString())
      .replace(/\bM\b/, this.monthNumber.toString())
      .replace(/\bDDDD\b/, this.dayNameOfWeek)
      .replace(/\bDD\b/, this.dayOfMonth.toString().padStart(2, '0'))
      .replace(/\bD\b/, this.dayNumberOfWeek.toString());
  }
}

Aucun commentaire:

Enregistrer un commentaire