Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mryechkin/2158fa33a7843de09b5579b1a387ff3a to your computer and use it in GitHub Desktop.
Save mryechkin/2158fa33a7843de09b5579b1a387ff3a to your computer and use it in GitHub Desktop.

Revisions

  1. @DeruiDENG DeruiDENG revised this gist Apr 22, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion TypeScript-JSDoc-Guides.md
    Original file line number Diff line number Diff line change
    @@ -218,7 +218,7 @@ type withGetImageFunctionality =

    ### Work with redux

    See **Source/Web/Scripts/dataStore/clubJetstar/index.js** for detailed examples for using redux in the code base
    See **Source/Web/Scripts/dataStore/club/index.js** for detailed examples for using redux in the code base

    #### Store

  2. @DeruiDENG DeruiDENG created this gist Oct 12, 2019.
    361 changes: 361 additions & 0 deletions TypeScript-JSDoc-Guides.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,361 @@
    # Using JSDOC-Based TypeScript

    ## Get Started

    ### Choose your editor

    * WebStorm, Rider
    * Partial support, not enough intelli hints
    * Toggle on TypeScript language service
    * VSCode
    * Full support
    * Working out-of-box
    * Emacs?



    ## Write the code

    ### Basic

    #### function

    1. Use @param to declare param type
    2. Use @returns to declare return type

    ``` javascript
    /**
    * @param {number} a
    * @param {number} b
    * @returns {number}
    */
    function add(a, b) {
    return a + b;
    }
    ```



    #### variable

    1. Use @type to declare the type of a variable

    2. Most of the time, it is not necessary to do it, since TypeScript can infer most of the type.

    ``` typescript
    /**
    * @type {(number|string)[]}
    */
    const c = [5];
    ```



    #### interface

    1. Use @typedef to declare interface

    2. Interface can be reused

    ```javascript
    /**
    * @typedef People
    * @prop {string} firstName
    * @prop {string} [lastName]
    */

    /**
    * @typedef {{firstName: string, lastName: string}} PeopleTwo
    */

    /**
    * @param {People} people
    */
    function getPeopleName(people){
    return people.firstName;
    }
    ```



    ### Work with React

    #### Functional component

    Exactly the same as declare a function

    ```jsx
    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 }) => (
    ...
    );
    ```

    #### Class component

    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.
    ```jsx
    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>
    );
    }
    }
    ```



    #### Higher order component

    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:

    ```typescript
    // 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


    #### Useful built-in type

    | 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<P>
    >
    > ComponentType<P>
    >
    > ComponentClass<P>
    >
    > FunctionComponent<P>
    >
    > ComponentProps<T>
    >
    > ReactHTMLElement
    >
    > MutableRefObject<ComponentName>


    ### Work with redux

    See **Source/Web/Scripts/dataStore/clubJetstar/index.js** for detailed examples for using redux in the code base

    #### Store

    You should declare the store type, which the reducers and selectors will rely on

    ```javascript
    /**
    * @typedef Profile
    * @prop {string} firstName
    * @prop {string} lastName
    * @prop {bool} isStarMember
    */

    /**
    * @typedef State
    * @prop {boolean} isLoggedIn
    * @prop {Profile} [profile]
    */

    /**
    * @typedef WholeState
    * @prop {State} account
    */
    ```

    ##### Explicitly declare the initial state

    This practice can help check the initialState is correct.

    ```typescript
    /**
    * @type {State}
    */
    const initialState = {
    isLoggedIn: true,
    profile: null,
    }
    ```



    #### Action and Reducers

    **You should declare the param of action creaters correctly**, so that you can always call the action creator functions correctly

    To achieve it:

    1. The type have to be declared as string constant
    2. Use createAction utility function
    3. In the project, some bolier-plate in JSDOC will ease your job

    ```javascript
    // 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 }),
    };
    ```



    ##### Reducer

    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.

    ```javascript
    /**
    *
    * @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;
    }
    };
    ```



    #### Selector

    **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

    ```javascript
    /**
    * Get booking total
    * @param {WholeState} state
    * @returns {number}
    */
    const getTotal = state => {
    // ...
    };
    ```



    ## Check The Code

    Option 1:

    Use WebStorm or VSCode

    Option 2:

    Config your Rider correctly

    Option 3:

    Run the command in the root folder:

    ```node
    yarn ts:check
    tsc .
    ```