Skip to content

Commit

Permalink
[typescript] Generic date type (mui#1966)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmtrKovalenko authored Jul 10, 2020
1 parent 26dcea5 commit c3c4231
Show file tree
Hide file tree
Showing 56 changed files with 1,161 additions and 1,061 deletions.
1 change: 1 addition & 0 deletions docs/layout/components/navigationMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const navItems = [
{
title: 'Guides',
children: [
{ title: 'TypeScript', href: '/guides/typescript' },
{ title: 'Accessibility', href: '/guides/accessibility' },
{ title: 'Form integration', href: '/guides/forms' },
{ title: 'CSS overrides', href: '/guides/css-overrides' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TextField from '@material-ui/core/TextField';
import { DateRangePicker, DateRange, DateRangeDelimiter } from '@material-ui/pickers';

function BasicDateRangePicker() {
const [selectedDate, handleDateChange] = React.useState<DateRange>([null, null]);
const [selectedDate, handleDateChange] = React.useState<DateRange<Date>>([null, null]);

return (
<DateRangePicker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Typography, TextField } from '@material-ui/core';
import { DateRangePicker, DateRangeDelimiter, DateRange } from '@material-ui/pickers';

function CalendarsDateRangePicker() {
const [selectedDate, handleDateChange] = React.useState<DateRange>([null, null]);
const [selectedDate, handleDateChange] = React.useState<DateRange<Date>>([null, null]);

return (
<Grid container direction="column" alignItems="center">
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/demo/daterangepicker/CustomRangeInputs.example.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import * as React from 'react';
import { DateRangePicker, DateRange } from '@material-ui/pickers';

function CustomRangeInputs() {
const [selectedDate, handleDateChange] = useState<DateRange>([null, null]);
const [selectedDate, handleDateChange] = React.useState<DateRange<Date>>([null, null]);

return (
<DateRangePicker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function getWeeksAfter(date: Moment | DateTime | Dayjs | Date, amount: number) {
}

function MinMaxDateRangePicker() {
const [selectedRange, handleDateChange] = React.useState<DateRange>([null, null]);
const [selectedRange, handleDateChange] = React.useState<DateRange<Date>>([null, null]);

return (
<DateRangePicker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '@material-ui/pickers';

function ResponsiveDateRangePicker() {
const [selectedDate, handleDateChange] = React.useState<DateRange>([null, null]);
const [selectedDate, handleDateChange] = React.useState<DateRange<Date>>([null, null]);

return (
<React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TextField from '@material-ui/core/TextField';
import { StaticDateRangePicker, DateRangeDelimiter, DateRange } from '@material-ui/pickers';

function StaticDateRangePickerExample() {
const [selectedDate, handleDateChange] = React.useState<DateRange>([null, null]);
const [selectedDate, handleDateChange] = React.useState<DateRange<Date>>([null, null]);

return (
<React.Fragment>
Expand Down
83 changes: 83 additions & 0 deletions docs/pages/guides/typescript.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import PageMeta from '_shared/PageMeta';

<PageMeta
title="TypeScript – @material-ui/pickers"
description="A guide of how to use @material-ui/pickers with TypeScript"
/>

## TypeScript guide

The date picker components come with built-in TypeScript definitions.
Here is some information about how to achive better experience with TypeScript.

<!-- https://stackoverflow.com/questions/62452160/material-datetimepicker-onchange-typescript-mismatch-error/62471345 -->

#### Nullable value

Any picker can return null in the `onChange` argument so if you are using `React.useState`, you **must** manually cast your default value as nullable. (e.g. `Date | null`)

```tsx
function MyComponent() {
const [value, setValue] = React.useState<Date | null>(new Date());

return (
<DatePicker
value={value}
onChange={newValue => setValue(newValue)}
renderInput={props => <TextField {...props} />}
/>
);
}
```

#### Generics

The components don't know which adapter you will pass to the `<LocalizationProvider />`. So any picker component is generic by default.
It means that it will try to infer the `value` and `onChange` type from the passed props.

```tsx
<DatePicker
value={new Date()}
// date will be inferred as `Date | null`
onChange={newValue => newValue?.getDate()}
/>
```

It also means that you can manually set value type thanks to the generic JSX sytax:

```tsx
<DatePicker<Date>
// You can pass any value right here that can be parsed
value={new Date()}
onChange={newValue => newValue?.getDate()}
/>
```

#### Important note on type inference!

If you are passing not typed value to the picker (such like `null`) we cannot properly infer the type for you. It is required to properly type the vaue:

```tsx
<DatePicker
value={null}
// @ts-expect-error `Property 'getDate' does not exist on type 'never'.`
onChange={newValue => newValue?.getDate()}
/>
```

In this case it is required to manually set proper [generic type manually](#generics).

```tsx
<DatePicker<Date>
value={null}
onChange={newValue => newValue?.getDate()}
/>

// Or cast the type for the `value` or `onChange` prop.

<DatePicker
value={null as Date | null}
// @ts-expect-error `Property 'getDate' does not exist on type 'never'.`
onChange={newValue => newValue?.getDate()}
/>
```
Loading

0 comments on commit c3c4231

Please sign in to comment.