An open API service indexing awesome lists of open source software.

https://github.com/taehwanno/props-type

Utility type that defines the type of the React component props through propTypes and defaultProps in TypeScript
https://github.com/taehwanno/props-type

prop-types react typescript

Last synced: 10 months ago
JSON representation

Utility type that defines the type of the React component props through propTypes and defaultProps in TypeScript

Awesome Lists containing this project

README

          

# props-type [![npm version](https://badge.fury.io/js/props-type.svg)](https://badge.fury.io/js/props-type) ![github actions](https://github.com/taehwanno/props-type/workflows/Node%20CI/badge.svg)

Utility type that defines the type of the React component props through `propTypes` and `defaultProps` in TypeScript.

If you want to know about story of creating this package, see [the blog post](https://medium.com/@taehwanno.dev/react-component-props-typing-with-proptypes-and-defaultprops-in-typescript-233eadb86314) (Korean)

**Table of contents**

- [Installation](#installation)
- [Prerequisite](#prerequisite)
- [Usage](#usage)
- [Type Inference](#type-inference)
- [Example](#example)
- [without `defaultProps`](#without-defaultprops)
- [with `defaultProps`](#with-defaultprops)
- [`null` or `undefined` in `defaultProps`](#null-or-undefined-in-defaultprops)
- [Limits](#limits)

## Installation

```shell
# with NPM
$ npm install props-type

# with Yarn
$ yarn add props-type
```

## Prerequisite

- `typescript` >= 2.8 (recommend 3.0+ because of [support for `defaultProps` in JSX](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#support-for-defaultprops-in-jsx))
- `@types/prop-types` >= 15.5.4

## Usage

```tsx
import PropsType from 'props-type';

// Without defaultProps
type Props = PropsType;

// With defaultProps
type Props = PropsType;
```

## Type Inference

### Optional (without `isRequired`)

#### without `defaultProps`

```tsx
const propTypes = { disabled: PropTypes.bool };
type Props = PropsType;
```

- Internal : `Props` type is `disabled: boolean | null | undefined`
- External : ``

#### with `defaultProps`

```tsx
const propTypes = { disabled: PropTypes.bool };
const defaultProps = { disabled: false };
type Props = PropsType;
```

- Internal : `Props` type is `boolean`
- External : ``

### Required (with `isRequired`)

#### without `defaultProps`

```tsx
const propTypes = { disabled: PropTypes.bool.isRequired };
type Props = PropsType;
```

- Internal : `Props` type is `disabled: boolean`
- External : ``

#### with `defaultProps`

```tsx
const propTypes = { disabled: PropTypes.bool.isRequired };
const defaultProps = { disabled: false };
type Props = PropsType;
```

- Internal : `Props` type is `disabled: boolean`
- External : ``

## Example

### without `defaultProps`

#### External

```tsx
const propTypes = {
className: PropTypes.string,
disabled: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
onDoubleClick: PropTypes.func,
};

type ButtonProps = PropsType;
```

```tsx
// Correct

// Invalid
// Property 'disabled' and 'onClick' is missing
// Property 'onClick' is missing
// Property 'disabled' is missing
```

- required : `disabled`, `onClick`
- optional : `className`, `onDoubleClick`

#### Internal

```tsx
function Button({ className, disabled, onClick, onDoubleClick }: ButtonProps) {
return (

);
}

Button.propTypes = propTypes;
```

- `className` type : `string | null | undefined`
- `disabled` type : `boolean`
- `onClick` type : `((...args: any[]) => any)`
- `onDoubleClick` type : `((...args: any[]) => any) | null | undefined`

### with `defaultProps`

#### External

```tsx
const propTypes = {
className: PropTypes.string,
disabled: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
onDoubleClick: PropTypes.func,
};

const defaultProps = {
className: 'primary',
onDoubleClick(event: React.MouseEvent) {},
};

type ButtonProps = PropsType;
```

```tsx
// Correct

) => {}} />
) => {}} />

// Invalid
// Property 'disabled' and 'onClick' is missing
// Property 'onClick' is missing
// Property 'disabled' is missing
{}} /> // Type '(a: number) => void' is not assignable to type '(event: MouseEvent) => void'
```

- required : `disabled`, `onClick`
- optional : `className`, `onDoubleClick`

#### Internal

```tsx
function Button({ className, disabled, onClick, onDoubleClick }: ButtonProps) {
return (

);
}

Button.propTypes = propTypes;
Button.defaultProps = defaultProps;
```

- `className` type : `string`
- `disabled` type : `boolean`
- `onClick` type : `((...args: any[]) => any)`
- `onDoubleClick` type : `(event: React.MouseEvent) => void`

### `null` or `undefined` in `defaultProps`

#### External

```tsx
const propTypes = {
className: PropTypes.string,
testId: PropTypes.string,
};

const defaultProps = {
className: null,
testId: undefined,
};

type ButtonProps = PropsType;
```

```tsx
// Correct

// Incorrect

```

- required : N/A
- optional : `className`, `testId`

#### Internal

```tsx
function Button({ className, testId }: ButtonProps) {
return ;
}

Button.propTypes = propTypes;
Button.defaultProps = defaultProps;
```

- `className` type : `string | null`
- `testId` type : `string | undefined`

## Limits

The prop type of `oneOf` in `prop-types` is not inferenced to union type.

```tsx
const propTypes = {
type: PropTypes.oneOf(['button', 'submit', 'reset']),
};

const defaultProps = {
type: 'button',
};

type ButtonProps = PropsType;

function Button({ type }: ButtonProps) {
return Button; // Type 'string | null | undefined' is not assignable to type '"button" | "submit" | "reset" | undefined'.
}

Button.propTypes = propTypes;
Button.defaultProps = defaultProps;
```

`type` prop is inferenced to `string` (not a `'button' | 'submit' | 'reset'` union type) because array literal in TypeScript are widen to specific type. If you want to inference `oneOf` as union type, this workaround can help you.

```tsx
type ButtonProps = PropsType & {
type: 'button' | 'submit' | 'reset';
};
```

If you use TypeScript 3.4+, it can be solved by using [`const` assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions).

```tsx
const propTypes = {
type: PropTypes.oneOf(['button', 'submit', 'reset'] as const),
};

const defaultProps = {
type: 'button' as const,
};

type ButtonProps = PropsType;
```

## Thanks

This package is inspired by [Brie Bunge](https://github.com/brieb) in [Adopting TypeScript at Scale, JSConf Hawaii 2019](https://www.youtube.com/watch?v=P-J9Eg7hJwE&feature=youtu.be&t=1063)

## License

MIT © [Taehwan Noh](https://github.com/taehwanno)