- WebStorm, Rider
- Partial support, not enough intelli hints
- Toggle on TypeScript language service
- VSCode
- Full support
- Working out-of-box
- Emacs?
- Use @param to declare param type
- Use @returns to declare return type
/**
* @param {number} a
* @param {number} b
* @returns {number}
*/
function add(a, b) {
return a + b;
}
-
Use @type to declare the type of a variable
-
Most of the time, it is not necessary to do it, since TypeScript can infer most of the type.
/**
* @type {(number|string)[]}
*/
const c = [5];
-
Use @typedef to declare interface
-
Interface can be reused
/** * @typedef People * @prop {string} firstName * @prop {string} [lastName] */ /** * @typedef {{firstName: string, lastName: string}} PeopleTwo */ /** * @param {People} people */ function getPeopleName(people){ return people.firstName; }
Exactly the same as declare a function
import React from 'react';
/**
* @typedef Props
* @prop {React.ReactNode} title
* @prop {React.ReactNode} [desc]
* @prop {React.ReactNode} [selectFlightPrompt]
* @prop {()=>void} [onLogin]
*/
/**
* @param {Props} props
*/
const InflowHeader = ({ title, desc, selectFlightPrompt, onLogin }) => (
...
);
Use @extends Component<Props, State> to declare a stateful component.
Component<Props, State> is a utility type that returns the type of React component given Props and States.
import React, { Component } from 'react';
/**
* @typedef Props
* @prop {any} resource
*/
/**
* @typedef State
* @prop {boolean} isDrawerOpen
*/
/**
* @extends Component<Props, State>
*/
class SampleContainer extends Component {
/** @type {State} */
state = {
isDrawerOpen: false,
};
constructor(props){
super(props);
}
render() {
return (
<React.Fragment>
...
</React.Fragment>
);
}
}
Declaring type of higher order component using JSDoc is challenging.
It is recommended to use type declaration files (*.d.ts) instead.
Higher order component can be categorized as:
- HOC that add new props to a component
- HOC that fill props into a component, e.g.
connect
from 'react-redux'
The following example shows how to declare these two type of higher order component:
// HOC that fill props into a component
type withGetImage =
<P extends { getImage: () => Promise<void> }>(
Component: React.ComponentType<P>,
) => React.ComponentType<Omit<P, 'getImage'>>;
// HOC that add new props to a component
type withGetImageFunctionality =
<P extends object>(
Component: React.ComponentType<P>,
) => React.ComponentType<P & { getImage: () => Promise<void> }>;
Want to learn more? Visit https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb for more info
propTypes | TS |
---|---|
count: propTypes.number.isRequired | {number} count |
count: propTypes.number | {number} [count] |
propTypes.bool | {boolean} |
propTypes.string | {string} |
propTypes.arrayOf(propTypes.number).isRequired | {number[]} |
propTypes.node | {React.ReactNode} |
propTypes.func | {(a: number, b: number)=>number} |
propTypes.shape({a: number.isRequired, b: number}) | {{a: number, b?: number}} |
Read more
I have found a few React built-in types quite useful, they are:
FunctionComponent
ComponentType
ComponentClass
FunctionComponent
ComponentProps
ReactHTMLElement
MutableRefObject
See Source/Web/Scripts/dataStore/club/index.js for detailed examples for using redux in the code base
You should declare the store type, which the reducers and selectors will rely on
/**
* @typedef Profile
* @prop {string} firstName
* @prop {string} lastName
* @prop {bool} isStarMember
*/
/**
* @typedef State
* @prop {boolean} isLoggedIn
* @prop {Profile} [profile]
*/
/**
* @typedef WholeState
* @prop {State} account
*/
This practice can help check the initialState is correct.
/**
* @type {State}
*/
const initialState = {
isLoggedIn: true,
profile: null,
}
You should declare the param of action creaters correctly, so that you can always call the action creator functions correctly
To achieve it:
- The type have to be declared as string constant
- Use createAction utility function
- In the project, some bolier-plate in JSDOC will ease your job
// The type has to be declared as constant in HERE
const ACCOUNT__INIT = 'ACCOUNT__INIT';
const actionCreators = {
/**
* You need to declare the param type for each action creator function.
* You also have to call the getAction util function
* @param {{isLoggedIn: boolean}} payload
*/
init: ({ isLoggedIn }) =>
getAction(ACCOUNT__INIT, { isLoggedIn }),
};
Declare the type of parameters state
and action
like the following code
Also declare the return value of the reducers, this will make sure the reducers are written correctly.
/**
*
* @param {State} state
* @param {DataStore.Action<typeof actionCreators>} action
* @returns {State}
*/
const reducers = (state = initialState, action) => {
switch (action.type) {
case ACCOUNT__INIT: {
// TS will guarantee that the payload has the isLoggedIn property
const { isLoggedIn } = action.payload;
return {
...state,
isLoggedIn,
};
}
default:
return state;
}
};
You should declare the return type of selectors, so that the user will not use the selectors incorrectly. The declaration of return type also act as a TDD as you can think the behavior of a function before you implement it.
- Declare type of state as
WholeState
- Declare the return value of complex selector function
/**
* Get booking total
* @param {WholeState} state
* @returns {number}
*/
const getTotal = state => {
// ...
};
Option 1:
Use WebStorm or VSCode
Option 2:
Config your Rider correctly
Option 3:
Run the command in the root folder:
yarn ts:check
tsc .