Monday, July 31, 2017

Redux combineReducers compiler error

If a Redux action is added with properties other than the type property, here it's payload..

export interface UserActions extends Action {
    type: UserKind;
    payload: UserPayload;
}

..TypeScript will raise an error on combineReducers:



To fix the compilation error, make the Redux action property be optional:

export interface UserActions extends Action {
    type: UserKind;
    payload?: UserPayload;
}


Result:




However, TypeScript won't be able to catch missed properties, it won't raise an error that we didn't pass the payload property on User 6:




To fix that, remove the optional indicator on Redux action:

export interface UserActions extends Action {
    type: UserKind;
    payload: UserPayload;
}


Result, TypeScript is now able to catch missed properties:




However, we are back to this error on combineReducers:




To comply with combineReducers requirement of Reducer actions containing just a type property only, create another interface that has optional properties (e.g., payload), and use that for the Redux reducer's action parameter (UserActionsNoPayload). And then on UserActions, inherit UserActionsNoPayload from UserActions:



Now, no more combineReducers compilation error, and the compiler is still able to catch missed properties:




Here are the TypeScript definitions for the routeToUser:

export interface StateProps {
    user: Dto.Client.User;
    component: Component;
    counter: Dto.Client.Counter;
}


export interface ActionProps {
    routeToUser: UserDispatcher;
    doCounter: CounterDispatcher;
}


interface Props extends StateProps, ActionProps {
}


export type Dispatcher = (action: UserActions | CounterActions) => void;
export type UserDispatcher = (action: UserActions) => void;
export type CounterDispatcher = (action: CounterActions) => void;


export default class ReduxFirstRouterApp extends React.Component<Props, {}> {



And here is how are the states and dispatchers are mapped as properties to a component via Redux's connect:
const mapStateToProps = ({user, component, counter}: StateProps) => ({user, component, counter});

const mapDispatchToProps = (dispatch: Dispatcher): ActionProps => ({
    routeToUser: (action: UserActions) => dispatch(action),
    doCounter: (action: CounterActions) => dispatch(action)
});

this.AppContainer = connect(mapStateToProps, mapDispatchToProps)(ReduxFirstRouterApp);


Happy Coding!


P.S.

At first, I tried creating UserActions with complete properties, and then made UserActionsNoPayload inherits UserActions and define payload as optional on UserActionsNoPayload; however, it's a compiler error:


Sunday, July 23, 2017

Inline destructuring

Found out a neat ES6 functionality, it can do destructuring of object right on parameters itself.

Below is how it is done on TypeScript:

function greetNoDestructuring(dto: {name: string, favoriteNumber: number}): void {    
    console.log(dto.name);
    console.log(dto.favoriteNumber);
}

function greetManualDestructuring(dto: {name: string, favoriteNumber: number}): void {
    const {name, favoriteNumber} = dto;

    console.log(name);
    console.log(favoriteNumber);
}

function greetInlineDestructuring({name, favoriteNumber}: {name: string, favoriteNumber: number}): void {
    console.log(name);
    console.log(favoriteNumber);
}


greetNoDestructuring({name: 'Kal', favoriteNumber: 7});
greetManualDestructuring({name: 'El', favoriteNumber: 6});
greetInlineDestructuring({name: 'Superman', favoriteNumber: 42});


Happy Coding!

redux-first-router 404



As with all client-side routing, when page is reloaded it will result to 404.

The easiest way to fix that is to indicate fallback page when 404 occurs, this is typically the index.html

On development environment that are using webpack-dev-server, just add the following:

devServer: {
    hot: true,
    proxy: {
        "/api": "http://localhost:3000"
    },
    historyApiFallback: {
        index: "/index.html"
    }
}


Aside from the above, on the fallback page indicate the base href:

<base href="/"/>



Happy Coding!

TypeScript + redux-first-router expected string class/function

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `App`

When that runtime error occurred, chances is the default is forgotten when importing Link since there is no TypeScript definition for redux-first-router-link yet.

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
const Link = require('redux-first-router-link');

To fix:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
const Link = require('redux-first-router-link').default;


Happy coding!

Thursday, July 20, 2017

TypeScript can enforce code correctness is so awesome

Following is a compile-time error:



To correct that, use 'onClick' not 'onclick':




Checked the TypeScript definition of prop function:




Don't know yet how extends keyof works. One thing for sure, it truly helps developers avoid mistakes.


Happy Coding!

TypeScript solves the industry's billion dollar mistake too

Reading about the advantage of Flow over TypeScript. Saw these slides:



With Flow, the error is caught at compile-time instead, which indeeds solves the billion dollar mistake:



TypeScript 2.0 already has the same functionality as Flow, it's just not the default. Just add "strictNullChecks": true to tsconfig.json.



With that settings, possible null/undefined errors can now be caught at compile-time:



If the type is explicitly added to the function it will complain that there is a path that could lead to undefined value:



Just include the undefined value in function's return type:



The inferred return type for function foo if return type is not included is string | undefined

Finally, to fix the undefined error:



Of course, if it can be guaranteed that a function returns a non-null/non-undefined value, no need to add undefined:




Happy Coding!



Wednesday, June 14, 2017

GPG failed to write commit object

Got an error:

error: gpg failed to sign the data fatal: failed to write commit object


It took me more than two hours to narrow down the cause of the error:

These does not work too:
MacBook-Air:~ jack$ gpg2 --list-secret-keys
gpg: can't connect to the agent: IPC connect call failed
MacBook-Air:~ jack$ gpg-agent --daemon
gpg-agent[1499]: /Users/jack/.gnupg/gpg-agent.conf:2: invalid option

Error is caused by wrong configuration in gpg-agent.conf:

$ cat ~/.gnupg/gpg-agent.conf 


For some reasons the second line was added to the configuration:
pinentry-program /usr/local/bin/pinentry-mac 
/usr/local/bin/pinentry-mac 


That should just configured with this:
pinentry-program /usr/local/bin/pinentry-mac 

After correcting the configuration, gpg-agent is now working:

MacBook-Air:~ jack$ gpg-agent -v --daemon
gpg-agent[2012]: listening on socket '/Users/jack/.gnupg/S.gpg-agent'
gpg-agent[2012]: listening on socket '/Users/jack/.gnupg/S.gpg-agent.extra'
gpg-agent[2012]: listening on socket '/Users/jack/.gnupg/S.gpg-agent.browser'
gpg-agent[2012]: listening on socket '/Users/jack/.gnupg/S.gpg-agent.ssh'
gpg-agent[2013]: gpg-agent (GnuPG) 2.1.21 started

And so is gpg2:
MacBook-Air:~ jack$ gpg2 --list-secret-keys
gpg-agent[2013]: handler 0x70000e9d4000 for fd 7 started
/Users/jack/.gnupg/pubring.gpg
------------------------------
sec   rsa2048 2016-12-12 [SC]


With that, committing with gpg signing shall work, must configure gpg to use gpg2 if it is not yet configured to gpg2:
MacBook-Air:~ jack$ git config --global gpg.program gpg2


Happy Coding!