Transform a class into an iterable by implementing [Symbol.iterator] as the generator function for a property

I am attempting to enhance the Month class to make it iterable from a property in the constructor. To achieve this, I have utilized this[Symbol.iterable] as a generator function within the constructor scope. This allows me to utilize the for...of loop and gain access to the sequence of days. While following this tutorial, I encountered a problem with iterator typing that I am unsure how to resolve. If it works in pure JavaScript, I believe it should work in TypeScript with the appropriate typing.

//class Month

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

  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());
  }
}

Answer №1

Finally, after an extensive search, I uncovered the solution:

class Month {
  language: string;
  monthName: string;
  monthNumber: number;
  year: number;
  numberOfDaysInMonth: number;

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

    this.monthName = day.month;
    this.monthNumber = day.monthNumber;
    this.year = day.year;
    this.numberOfDaysInMonth = monthsLength[this.monthNumber - 1];

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

  *[Symbol.iterator](): Iterator<Day> {
    let number = 1;
    yield this.getDay(number);
    while (number < this.numberOfDaysInMonth) {
      ++number;
      yield this.getDay(number);
    }
  }

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Arrange the items that are missing from Array B to be located at the bottom of Array A, organized in a file tree structure

I have two arrays containing different types of objects. Each object in the arrays has a title assigned to it. My goal is to compare these two arrays based on their titles and move any files that are not included in the bottom part of the fileStructure arr ...

Angular 2 ngSubmit triggers unexpectedly on occasions when it is not supposed to

Currently, I am working on developing an Ionic 3 application with Angular 2 and TypeScript. In the app, there is a form that is responsible for sending data to our server. The issue I am facing is that whenever I click on the following button: <butto ...

What is the proper way to combine two arrays containing objects together?

I am faced with the challenge of merging arrays and objects. Here is an example array I am working with: [ { name: "test", sub: { name: "asdf", sub: {} } }, { name: "models", sub: {} } ] ...

Error in JSON format detected by Cloudinary in the live environment

For my upcoming project in Next.js, I have integrated a Cloudinary function to handle file uploads. Here is the code snippet: import { v2 as cloudinary, UploadApiResponse } from 'cloudinary' import dotenv from 'dotenv' dotenv.config() ...

Dynamic Variable Typing in TypeScript: Adjusting variable types on the fly

I am working with 2 variables named locals and visitants. These variables can either be of type PlayerDto or TeamDto, which will be determined by a third variable called competitor_type. If competitor_type is player, then I need to assign a list of Players ...

Utilizing Router Outlet in Angular to Access API Data

I've encountered an issue where I can't pass parent data from the ngOnInit route params to my child component, user-seminar. After some research and searching on Google, I found a solution involving services. To address this problem, I modified ...

Is it advisable to use an if statement or question mark in TypeScript to prevent the possibility of a null value?

Currently delving into TypeScript and exploring new concepts. I encountered a scenario where inputRef.current could potentially be null, so I opted to directly use a question mark which seems to work fine. However, in the tutorial video I watched, they use ...

Implementing React Localized Global Styling

Is there a way to confine the global styles of a module to just one file? For example, I want to adjust the width of an element in a react module but only within one specific file. Unfortunately, inline styles are not an option :/ Edit for clarification: ...

The parameter type 'NextHandleFunction' does not match the expected type 'PathParams' in the argument

After successfully setting up TypeScript with a basic Express server, I've encountered an issue. import bodyParser from 'body-parser'; import express, { Express } from 'express'; const app: Express = express(); app.use(bodyParser ...

Hover Effect for 3D Images

I recently came across an interesting 3D Hover Image Effect that I wanted to implement - https://codepen.io/kw7oe/pen/mPeepv. After going through various tutorials and guides, I decided to try styling a component with Materials UI and apply CSS in a differ ...

Closing a tab in another part of the session using Typescript and Angular

Looking for a solution on how to close a tab within an Angular session that was opened from somewhere else in the same session. For instance: In Component A this.window = this.windowToken.open('Some URL', 'Some Tab Name', 'Some ...

What is the cause of the error message "property 'map' is undefined"?

I am currently working on a service that looks like this: public getUsers() { return this.httpClient.get(environment.BASE_URL + `api/all`); } In my component, I have implemented the ngx-bootstrap table to display the user data. import { Component, OnI ...

Is it possible to apply Mat-card to an Anchor Element?

How can I make a mat-card clickable and show a link cursor when hovered over? I have multiple cards and when I click on one, I want to navigate to another page. What is the best way to achieve this? Is it acceptable to use the following code in my templat ...

The issue with dispatching actions in TypeScript when using Redux-thunk

As a beginner in TypeScript, I apologize if my question seems silly, but I'll ask anyway: I'm attempting to make an async call getUsersList(), but the issue is that it's not triggering the dispatch (it's not logging "hello"). It worked ...

"Encountering issues with getStaticPaths not generating any paths

I have a folder named data which contains a file called events.ts: export const EventsData: Event[] = [ { name: 'School-Uniform-Distribution', images: ['/community/conferences/react-foo.png', "/community/conferences/react ...

JavaScript Class Emit Signal for establishing a sequence of interconnected events

My Vue project includes a JavaScript class specifically for mobile devices. I'm looking to have this class emit a signal once the property 'hasEnded' is set to True for my object. How can I achieve this and chain together other events based ...

How can I pass DOCUMENT in Angular?

In my directive, I use dependency injection to access the DOCUMENT and set up an event listener: constructor(@Inject(DOCUMENT) private document: Document) {} ngOnInit() { this.document.addEventListener('click', this.clicked, true); } @Bound ...

Having trouble with triggers: Unable to locate the module 'csv-parse/sync' for parsing

Currently, I am utilizing Firebase functions to create an API that is capable of parsing CSV files. However, whenever I attempt to utilize csv-parse/sync instead of csv-parse, my deployment to Firebase Functions encounters a failure with the subsequent er ...

Using Typescript with Material UI Select

I have implemented a dropdown menu using Material UI select labeled "Search By." When the menu is clicked, it displays a list of options. I aim to store the selected option and update the label "Search By" with the chosen option. export default function U ...

What is the reason behind TypeScript classifying the argument type from the function as 'never'?

Presented here is the combined type of two signatures for the filter function and the function itself. type Filter = { (arr: string[], f: (item: string) => boolean): string[] (arr: number[], f: (item: number) => boolean): number[] } let filter: ...