mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.08k stars 1.26k forks source link

[pickers] AdapterDateFnsV3: Wrong types generated #13226

Closed alexey-kozlenkov closed 3 months ago

alexey-kozlenkov commented 4 months ago

Steps to reproduce

Link to live example: (https://codesandbox.io/p/sandbox/brave-matsumoto-9t8tqx?file=%2Fsrc%2FDemo.tsx%3A16%2C2)

Steps:

  1. Open sandbox
  2. Check produced TS type of const res in test function
  3. Or open AdapterDateFnsV3 file

Current behavior

Return types of AdapterDateFnsV3 functions are mostly any instead of their actual values

image

Expected behavior

Correspond to MuiPickersAdapter interface

Context

I'm trying to write an extended version of DateUtilsAdapterV3 with some custom utility functions and they reuse function if DateUtilsAdapterV3, which provides wrong typings.

Your environment

Executed in a devbox: https://codesandbox.io/p/devbox/y8p8k9?file=%2Fsrc%2FDemo.tsx%3A5%2C48

npx @mui/envinfo ``` Don't forget to mention which browser you used. System: OS: Linux 6.1 Debian GNU/Linux 12 (bookworm) 12 (bookworm) Binaries: Node: 20.9.0 - /usr/local/bin/node npm: 9.8.1 - /usr/local/bin/npm pnpm: 8.10.2 - /usr/local/share/npm-global/bin/pnpm Browsers: Chrome: Not Found npmPackages: @emotion/react: latest => 11.11.4 @emotion/styled: latest => 11.11.5 @mui/material: next => 6.0.0-alpha.8 @mui/x-date-pickers: latest => 7.5.1 @types/react: latest => 18.3.2 react: latest => 18.3.1 react-dom: latest => 18.3.1 typescript: latest => 5.4.5 ```

Used browser: Version 125.0.6422.61 (Official Build) (arm64)

Search keywords: adapterdatefnsv3 date-fns date-fns-v3

michelengelen commented 4 months ago

Thanks @alexey-kozlenkov for opening this issue. 👍🏼 I did add this to the board for the team to estimate and fix.

alexey-kozlenkov commented 3 months ago

@michelengelen I made a POC with a possible option to solve this, if team could have a look and decide if that's viable would be great

LukasTy commented 3 months ago

@alexey-kozlenkov Thank you for your contribution! 🙏 I'm not sure that your suggested approach is going to work, because that is a massive breaking change and request from users to install the date-fns v3 aliased in their applications. If they don't do it, the result is:

Screenshot 2024-05-31 at 17 01 35

We'll have to look for an alternative solution. 👍

alexey-kozlenkov commented 3 months ago

Ah, I see, thanks @LukasTy. I'll close that PR

LukasTy commented 3 months ago

We've decided to try an approach of adding explicit function returns in hopes of fixing this problem in the built packages. This would need to be done for both regular and V3 adapters as changing the date-fns version would move the problem to the AdapterDateFns.

An example of the idea:

- public isSameYear = (value: Date, comparing: Date) => {
+ public isSameYear = (value: Date, comparing: Date): boolean => {

@alexey-kozlenkov would you be interested in taking care of this problem with the suggested approach? 🤔

alexey-kozlenkov commented 3 months ago

@LukasTy yeah, sure

eglavin commented 3 months ago

Would this be why I'm unable to assign anything to a date picker? I'm getting this error when the type is Date | null: image

Or would I be experiencing a different issue?

Relevant dependencies:

"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.5",
"@mui/icons-material": "5.15.20",
"@mui/material": "5.15.20",
"@mui/x-date-pickers": "7.6.0",
"date-fns": "3.6.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",

Typescript error:

Type 'Date | null' is not assignable to type 'null | undefined'.
  Type 'Date' is not assignable to type 'null | undefined'.ts(2322)
usePickerValue.types.d.ts(179, 5): The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & DesktopDateTimePickerProps<never, false> & RefAttributes<HTMLDivElement>'

Weirdly not running into this issue on a code sandbox though: https://codesandbox.io/p/devbox/x8lcsv?file=%2Fsrc%2FDemo.tsx%3A11%2C55

LukasTy commented 3 months ago

@eglavin do you have a AdapterDateFnsV3 import somewhere in your application? 🤔 And is your fromDate really typed correctly (Date | null) in your application?

eglavin commented 3 months ago

Ahh ok yeah importing it directly in the project its self seems to make it work correctly. kinda breaks what we're doing though.

Essentially we have a micro-frontend stack which means our theme config is in a separate library which contains our theme providers like so:

import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
import { StyledEngineProvider, ThemeProvider as MuiThemeProvider } from "@mui/material/styles";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";

...

export const ThemeProvider = ({ children }: { children: React.ReactNode }): JSX.Element => {
    const [activeTheme, setActiveTheme] = useState<Theme>("dark");
    const selectedTheme = useMemo(() => generateTheme(activeTheme), [activeTheme]);

    const snackbarRef = useRef<SnackbarProvider | null>(null);

    return (
        <ThemeContext.Provider value={{ activeTheme, setActiveTheme }}>
            <StyledEngineProvider injectFirst>
                <MuiThemeProvider theme={selectedTheme}>
                    <EmotionThemeProvider theme={selectedTheme}>
                        <LocalizationProvider dateAdapter={AdapterDateFns}>
                            <SnackbarProvider
                                ref={snackbarRef}
                                maxSnack={3}
                                preventDuplicate
                                anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
                                action={(key) => <SnackbarAction id={key} snackbarRef={snackbarRef.current} />}
                            >
                                {children}
                            </SnackbarProvider>
                        </LocalizationProvider>
                    </EmotionThemeProvider>
                </MuiThemeProvider>
            </StyledEngineProvider>
        </ThemeContext.Provider>
    );
};

This ThemeProvider component is then bundled in an internal npm package which gets installed onto each micro-frontend, how does typescript then know which adapter is being used? is there a mechanism to tell typescript which adapter is being used without importing it into every frontend app?

eglavin commented 3 months ago

Ok I see it, you have this interface declared in the adapter function. I'll be able to use this to get it to register, thanks for you're help @LukasTy!

declare module '@mui/x-date-pickers/models' {
  interface PickerValidDateLookup {
    'date-fns': Date;
  }
}
LukasTy commented 3 months ago

Are all your frontend apps using AdapterDateFns? In which case feel free to follow suggestions in https://github.com/mui/mui-x/issues/12640#issuecomment-2039434173 or later messages. import type {} from '@mui/x-date-pickers/AdapterDateFnsV3' is slightly more fool proof, but declaring your own module as you mentioned is also a viable choice. 👍

eglavin commented 3 months ago

Most if not all do yes, this will work for now, thanks again! 😁

github-actions[bot] commented 3 months ago

:warning: This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue. Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.

@alexey-kozlenkov: How did we do? Your experience with our support team matters to us. If you have a moment, please share your thoughts in this short Support Satisfaction survey.