Fusion.js supports Flow out of the box for static type checking.
JavaScript lacks language level support for static types, making it difficult to ensure that code you write has the correct types at compile time. Although you can write JavaScript code without any type annotations and have it work, there is the risk that bad code leads to malformed inputs being provided to methods or functions that have to then either gracefully fail (e.g. runtime type checking with error handling) or fail hard (e.g. unhandled exceptions). In the worst case, a mismatched type goes unchecked and cascades to a failure down the road leading to lengthy debugging and unintended side effects.
At its core, static type checking aims to verify and enforce that values being used by your code meet the invariant conditions specified by the types at compile time. This offers a number of advantages over dynamic typing:
Flow is not the only solution for static type checking in JavaScript. It is, however, an elegant solution that provides benefits without having to change any of your existing code. Flow has the ability to infer types within your code, ensuring that it can help catch type errors from the get go. And adding type annotations allows for incrementally improving type coverage. We recommended that you use Flow since Fusion.js provides types for core components of the framework that can be used in your application.
If you are new to Flow, we recommended that you check out the Getting Started and Type Annotations documentation.
If you use Visual Studio Code as your primary IDE, check out the Flow for Visual Studio Code extension.
Annotating React components with type annotations can be very powerful for ensuring your components are used as expected. To illustrate this, let's write a very simple component that displays a user's name.
// src/components/display-name
// @flow
import React from 'react';
type Props = {|
firstName: string,
lastName: ?string // may be undefined, if last name not provided
|}
class DisplayNameComponent extends React.Component<Props> {
render() {
const fullName = `${this.props.firstName} ${this.props.lastName || ''}`;
return <div>{fullName}</div>;
}
}
If we run flow run check
, we'd expect to see find no errors. Let's see what happens if we try to also display a user's age.
class DisplayNameComponent extends React.Component<Props> {
render() {
const fullName = `${this.props.firstName} ${this.props.lastName || ''}`;
const age = this.props.age; // type error!
return <div>{fullName}</div>;
}
}
In this case, we get the an error that the property age
was not found in Props
, as illustrated here.
We recommended that you check out the Flow + React documentation.
To see how we can leverage built in Fusion.js types, let's write a very simple Fusion.js plugin that logs a random cat fact on every request to /log-cat-fact
.
// src/plugins/cat-facts.js
// @flow
import {createPlugin} from 'fusion-core';
import {LoggerToken} from 'fusion-tokens';
import type {Context} from 'fusion-core';
/* Helpers */
const FACTS: Array<string> = [ // provided by: https://catfact.ninja/fact
"A 2007 Gallup poll revealed that both men and women were equally likely to own a cat.",
"Most cats adore sardines.",
"Cats have been domesticated for half as long as dogs have been.",
"The average litter of kittens is between 2 - 6 kittens.",
];
function getCatFact(): string {
return FACTS[Math.floor(Math.random() * FACTS.length)];
}
export default createPlugin({
deps: {logger: LoggerToken},
middleware: deps => (ctx: Context, next: () => Promise<*>) => {
if(ctx.url === '/log-cat-fact') {
deps.logger.log(getCatFact());
}
return next();
}
});
Let's break this down. We import a type {Context}
from fusion-core
which is used in our middleware signature. This let's us ensure safe accessibility of the ctx
object. We are also using the dependency injected logger which is registered with LoggerToken
. If we look at the type of logger
, we would expect to see:
// defined in https://github.com/fusionjs/fusion-tokens/blob/master/src/index.js#L24
export type Logger = {
log(level: string, arg: any): void,
error(arg: any): void,
warn(arg: any): void,
info(arg: any): void,
verbose(arg: any): void,
debug(arg: any): void,
silly(arg: any): void,
};
If we tried to do deps.logger.notALogMethod(...)
we would expect a type error. Fusion.js will automatically hook up these injected types when they are available, ensuring more seamless type safety across the Fusion.js ecosystem.