Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation on how to globally setup dayjs with typescript and plugins #1577

Closed
kjellski opened this issue Jul 14, 2021 · 19 comments
Closed

Comments

@kjellski
Copy link

Describe the bug
After reading the current section about using dayjs with typescript I have to redo the steps from there on every file I want to use a plugin in: https://day.js.org/docs/en/installation/typescript

Expected behavior
Setup instructions on how to setup dayjs once globally and have it recognize all used plugin types globally

Information

  • Day.js Version latest

I would love to contribute, but from reading a ton of tickets I still can't figure out what the setup should be. If we can clear that up, I'll prepare a PR 👍

@addisonElliott
Copy link

Here's how I use Day.js in my codebase. This is in a dayjs.ts file and it is imported once from my main file (index.ts). After that, I don't have to import the plugin files or even this file again in order to get Typescript definitions.

/* eslint-disable func-names */
import dayjs from 'dayjs';
import 'dayjs/plugin/utc';
import 'dayjs/plugin/duration';

dayjs.extend(require('dayjs/plugin/utc'));
dayjs.extend(require('dayjs/plugin/duration'));

So, to be clear, I'm able to write code like this in completely different files and it works for me:

import dayjs from 'dayjs';

dayjs.utc('2021-07-14T00:00:00Z');

@kjellski
Copy link
Author

@addisonElliott that sounds fantastic, it's exactly what I would be looking for. Could you post something about your setup? For example your tsconfig.json?

@addisonElliott
Copy link

Here's my tsconfig.json, but there's nothing special about it I don't think.

{
  "compilerOptions": {
    "target": "es2019",
    "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"],
    "module": "commonjs",
    "incremental": true,
    "outDir": "./dist",
    "allowJs": true,
    "strict": true,
    "esModuleInterop": true,
    "sourceMap": true,
    "forceConsistentCasingInFileNames": true
  }
}

Are you having troubles with the setup I described?

@BePo65
Copy link
Contributor

BePo65 commented Jul 15, 2021

During my tests with dayjs I created a small demo project for using dayjs with typescript; it can be found on github.

Perhaps this will help answering some questions.

@kjellski
Copy link
Author

kjellski commented Jul 16, 2021

During my tests with dayjs I created a small demo project for using dayjs with typescript; it can be found on github.

Perhaps this will help answering some questions.

@BePo65 This is unfortunately not what I'm thinking about. I think it would be nice to have dayjs setup once and then make it project globally aware of having the types applied by certain extensions.

@kjellski
Copy link
Author

Here's my tsconfig.json, but there's nothing special about it I don't think.

{
  "compilerOptions": {
    "target": "es2019",
    "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"],
    "module": "commonjs",
    "incremental": true,
    "outDir": "./dist",
    "allowJs": true,
    "strict": true,
    "esModuleInterop": true,
    "sourceMap": true,
    "forceConsistentCasingInFileNames": true
  }
}

Are you having troubles with the setup I described?

Yes, if I'm setting this up in e.g. an index.ts at the root of the project with some plugins, then somewhere down the line using import dayjs from 'dayjs' will just not recognize the applied plugins signatures from their types. But that's what's working fine for you, right?

@addisonElliott
Copy link

Yes, and I use this concept daily with other packages besides Day.js.

This is a feature for how module augmentation. Module augmentation, which is extended existing packages, is used by Day.js to add functionality to the existing Day.js type definitions. From my understanding, once this file is imported, the module is augmented everywhere. The Typescript compiler doesn't conditionally augment modules or do it on a file-by-file basis.

For example, I've augmented the built-in Array class to add some helper functions. I placed the augmented files in .d.ts file and Typescript knows to import them and utilize them. I don't have to import them anywhere else to see the new functions in the Array class.

Can you create a sample project where you're having the issue so I can look it over?

@BePo65
Copy link
Contributor

BePo65 commented Jul 16, 2021

Of course you know that this is not the way typescript wants to handle modules (see typescript documentation), but we cannot always start from scratch, if we are working on our projects ;-).

So what you are asking for is something like the solution mentioned in this comment, isn't it?

But IMHO this is kind of a hack and should not be part of the official documentation of a package like dayjs.

@iamkun
Copy link
Owner

iamkun commented Jul 17, 2021

The plugins will update the global dayjs instance.

That is to say, in a single page application, if you extend the plugins in your main entry file like index.js,

There's no need to do the same thing on other pages in your app,

only import dayjs from 'dayjs' , and use the added methods by the plugins.

@afacode
Copy link

afacode commented Aug 2, 2021

Official documents
Import and use in your Typescript file

import * as dayjs from 'dayjs'
import * as isLeapYear from 'dayjs/plugin/isLeapYear' // import plugin
import 'dayjs/locale/zh-cn' // import locale

dayjs.extend(isLeapYear) // use plugin
dayjs.locale('zh-cn') // use locale

Replace with the following

import dayjs from 'dayjs'
import isLeapYear from 'dayjs/plugin/isLeapYear' // import plugin
import 'dayjs/locale/zh-cn' // import locale
dayjs.extend(isLeapYear) // use plugin
dayjs.locale('zh-cn') // 

@kjellski
Copy link
Author

kjellski commented Aug 3, 2021

Thanks for the hints @addisonElliott - it must be due to our setup then.

@iamkun that I know, and it works nicely. But it isn't updating the types on the imported dayjs file afterwards. So when I try to use plugin methods, I'm getting compile errors from typescript.

I'm closing this due to the fact that I might be misconfiguring something on my end. Thanks for the help everyone!

A working example of this

@kjellski kjellski closed this as completed Aug 3, 2021
@j-a-h-i-r
Copy link

One additional question. Does extending dayjs with plugin(s) affect imported modules too?

Let me explain, in my NextJs project, I am adding some plugins to dayjs in the _app.tsx file (the root file basically).

import dayjs from 'dayjs'
import updateLocale from "dayjs/plugin/updateLocale";
import relativeTime from "dayjs/plugin/relativeTime";

// update locale
dayjs.extend(relativeTime);
dayjs.extend(updateLocale);
dayjs.updateLocale("en", {
  relativeTime: {
    future: "in %s",
    past: "%s ago",
    s: "a few modified seconds",
    m: "1 minute",
    mm: "%d modified minutes",
    h: "1 hour",
    hh: "%d modified hours",
    d: "1 day",
    dd: "%d days",
    M: "a month",
    MM: "%d months",
    y: "a year",
    yy: "%d years",
  },
});

Now, I have a component library where I have pure components. This lives outside of the NextJS application and is imported as an external dependency. As this is a component library, it does not have any "root" file per se. I use dayjs in this library. So will the added plugins also apply to the dayjs in this library too? For example,

// @ComponentLibrary/Test.tsx
import dayjs from 'dayjs';

export function MyLib() {
    return dayjs(new Date()).fromNow()
}

Will this print a few seconds ago (the default) or a few modified seconds (the result of applying the plugins)?

@BePo65
Copy link
Contributor

BePo65 commented Oct 4, 2021

IMHO it will output a few modified seconds as the code of the external component runs in the scope of your _app.tsx file and module features are imported via import into the scope of a single script - in your case this is your tsx file (see 'mdn' on JavaScript modules).

Update
I created a small demo to show what happens if you extend dayjs in the main program as asked by @j-a-h-i-r.
You can find the demo in github.

If this demo program is started, the output is:

$ node src/index.js

dayjs in this program gets 'a few modified seconds ago' from dayjs.fromNow()
dayjs in the imported package gets 'a few modified seconds ago' from the formatNow

@ala-garbaa-pro
Copy link

This is the right way: 😊👌

import type { Dayjs } from "dayjs";
import dayjs from "dayjs";
...

const formatDate = (date: Date): string => {
  const dayObj: Dayjs = dayjs(date);
  return `${dayObj.format("MMM D, YYYY")}`;
};

@JonathanSTH
Copy link

JonathanSTH commented Aug 2, 2023

Here's how I use Day.js in my codebase. This is in a dayjs.ts file and it is imported once from my main file (index.ts). After that, I don't have to import the plugin files or even this file again in order to get Typescript definitions.

/* eslint-disable func-names */
import dayjs from 'dayjs';
import 'dayjs/plugin/utc';
import 'dayjs/plugin/duration';

dayjs.extend(require('dayjs/plugin/utc'));
dayjs.extend(require('dayjs/plugin/duration'));

So, to be clear, I'm able to write code like this in completely different files and it works for me:

import dayjs from 'dayjs';

dayjs.utc('2021-07-14T00:00:00Z');

@kjellski
so sorry to bother you. i'd love to actually see this. can't really seem to make it work like you've indicated. do you have a sample repo or even just the code on what needs to happen? using cypress v12

@yannxaver
Copy link

yannxaver commented Oct 31, 2023

I'm using next.js 14 app router. Extending day.js globally does not work.

// layout.tsx
import dayjs from 'dayjs';
import AdvancedFormat from 'dayjs/plugin/advancedFormat';
dayjs.extend(AdvancedFormat);

@ghostlexly
Copy link

I'm using next.js 14 app router. Extending day.js globally does not work.

// layout.tsx
import dayjs from 'dayjs';
import AdvancedFormat from 'dayjs/plugin/advancedFormat';
dayjs.extend(AdvancedFormat);

On NextJS, you have to create a new lib file and export it.

import dayjs from "dayjs";
import "dayjs/locale/fr";
import utc from "dayjs/plugin/utc";
import customParseFormat from "dayjs/plugin/customParseFormat";

const dayjsExt = dayjs;

dayjs.locale("fr");
dayjs.extend(utc);
dayjs.extend(customParseFormat);

export { dayjsExt };

@wherehows
Copy link

I'm using next.js 14 app router. Extending day.js globally does not work.

// layout.tsx
import dayjs from 'dayjs';
import AdvancedFormat from 'dayjs/plugin/advancedFormat';
dayjs.extend(AdvancedFormat);

On NextJS, you have to create a new lib file and export it.

import dayjs from "dayjs";
import "dayjs/locale/fr";
import utc from "dayjs/plugin/utc";
import customParseFormat from "dayjs/plugin/customParseFormat";

const dayjsExt = dayjs;

dayjs.locale("fr");
dayjs.extend(utc);
dayjs.extend(customParseFormat);

export { dayjsExt };

What if have to support i18n?

do you know how to import only the necessary languages instead of importing all languages as shown below?

import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import "dayjs/locale/ko";
import "dayjs/locale/zh-cn";
import "dayjs/locale/ja";
import "dayjs/locale/en";

const dayjsExt = dayjs;
dayjs.extend(relativeTime);

export default dayjsExt;

@ghostlexly
Copy link

I'm using next.js 14 app router. Extending day.js globally does not work.

// layout.tsx
import dayjs from 'dayjs';
import AdvancedFormat from 'dayjs/plugin/advancedFormat';
dayjs.extend(AdvancedFormat);

On NextJS, you have to create a new lib file and export it.

import dayjs from "dayjs";
import "dayjs/locale/fr";
import utc from "dayjs/plugin/utc";
import customParseFormat from "dayjs/plugin/customParseFormat";

const dayjsExt = dayjs;

dayjs.locale("fr");
dayjs.extend(utc);
dayjs.extend(customParseFormat);

export { dayjsExt };

What if have to support i18n?

do you know how to import only the necessary languages instead of importing all languages as shown below?

import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import "dayjs/locale/ko";
import "dayjs/locale/zh-cn";
import "dayjs/locale/ja";
import "dayjs/locale/en";

const dayjsExt = dayjs;
dayjs.extend(relativeTime);

export default dayjsExt;

You can support multiple languages like this after importing them:

// example for DE language
dayjs().locale('de').format(); 

// switch to FR language
dayjs().locale('fr').format(); 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests