Convincing a Type Sceptic

Up Logo

Flow

Flow is a static type checker for JavaScript.

https://flow.org/

JavaScript


// js

function sum(a, b) {
  return a + b
}






function render(customer) {
  return (
    <div>
      <h1>Hi {customer.firstNamr} 👋</h1>
      <p>You've got ${money(customer.balance).format()}</p>
    </div>
  )
}

JavaScript + types


// @flow

function sum(a: number, b: number) {
  return a + b
}

type CustomerType {
  firstName: string,
  balance: number
}

function render(customer: CustomerType) {
  return (
    <div>
      <h1>Hi {customer.firstNamr} 👋</h1>
      <p>You've got ${money(customer.balance).format()}</p>
    </div>
  )
}

JavaScript + types


// @flow

function sum(a: number, b: number): number {
  return a + b
}

type CustomerType {
  firstName: string,
  balance: number
}

function render(customer: CustomerType): React.Node {
  return (
    <div>
      <h1>Hi {customer.firstNamr} 👋</h1>
      <p>You've got ${money(customer.balance).format()}</p>
    </div>
  )
}

Install and run

~/projects/my-killer-app
$ npm install -g flow-bin
$ flow init
$ flow

Or install a plugin in your editor

Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/test.js:15:7

Cannot get customer.firstNamr because property firstNamr is missing in CustomerType [1].

  [1] 12│ function render(customer: CustomerType): React.Node {
      13│   return (
      14│     <div>
      15│       <h1>Hi {customer.firstNamr} 👋</h1>
      16│       <p>You've got ${money(customer.balance).format()}</p>
      17│     </div>
      18│   );

Excuses

"I'm comfortable with JavaScript's dynamic types. Truthy, falsy values, type coercion, types are unnecessary."

"I've been writing React code for years without prop types or static types and it's worked fine."

"Types add noise and make the program more obscure"

"There's an ongoing maintenance burden"

"Never bet against JavaScript"

Challenges

"I've never once had a case where I've said thankfully flow was there"

"It's a shame that flow is making the code here more complex."

"Some of the types we're adding have no benefit at all."

import request from './request';
import {apiRoot} from './apiRoot';

type CancelChequeRequestType = {
  accountId: string,
  chequeAmount: number,
  chequeNumber: number,
  issueDate: string,
  payeeName: string,
  reason: string,
  reasonOther: ?string,
};

export const submitCancelCheque = (formData: CancelChequeRequestType) =>
  request(`${apiRoot}/cheque_cancellations`, {
    type: 'POST',
    dataType: 'json',
    data: formData,
  });
import request from './request';
import {apiRoot} from './apiRoot';

type CancelChequeRequestType = {
  monkey: string,
  jerky: Object
}






export const submitCancelCheque = (formData: CancelChequeRequestType) =>
  request(`${apiRoot}/cheque_cancellations`, {
    type: 'POST',
    dataType: 'json',
    data: formData,
  });

Progress

"appease the linter"

"flow for Andy ❤️"

"flow saved my bacon"

#1 Refactoring

type CommonTransferType = {
  fromAccountId: string,
  fromAccountName: string,
  fromAccountBsb: string,
  fromAccountNumber: string,
  toAccountId: string,
  toAccountName: string,
  toAccountBsb: string,
  toAccountNumber: string,
};

function renderTransfer(transfer: CommonTransferType) {

}
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/test.js:14

Cannot get transfer.from_account_id because property from_account_id is missing in CommonTransferType [1].

    12| function render(transfer: CommonTransferType) {
    13|   const params = {
[1] 14|     from_id: transfer.from_account_id,
    15|     to_id: transfer.toAccountId,
    16|   };

#2 Mistakes


        return {[field.name]: 'Please select a date'};
      }
      if (!value.match(/^\d{4}[-]\d{1,2}[-]\d{1,2}$/)) {
-       return i18n.t('shared.errors.date.invalid_format', {values: {format: 'DD/MM/YYYY'}});
+       return {[field.name]: 'Please use the following date format: DD/MM/YYYY'};
      }
      if (!moment(value, 'YYYY-MM-DD').isValid()) {
-       return i18n.t('shared.errors.date.invalid');
+       return {[field.name]: 'Please enter a valid date'};
      }

      return true;

Inferred types

     Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/test.js:8

     string [1] is not an object.

     1| // @flow
     2|
     3| function validate(field) {
[1]  4│   return "An error";
     5│ }
     6│
     7│ const validationResult = validate("test");
     8│ const errors = {
     9│   ...validationResult
    10│ };
    11│

#3 Cleaner code

Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/test.js:7

Cannot call validate because no arguments are expected by function [1].

 [1]  3│ function validate() {
      4│   return {};
      5│ }
      6│
      7│ const validationResult = validate("test");

#4 propTypes

import React from 'react';
import PropTypes from 'prop-types';

class MyComponent extends React.Component {
  static propTypes = {
    foo: PropTypes.number.isRequired,
    bar: PropTypes.string,
  };

  render() {
    return <div>{this.props.bar}</div>;
  }
}

#4 propTypes

import * as React from 'react';

type Props = {
  foo: number,
  bar?: string,
};

class MyComponent extends React.Component<Props> {
  render() {
    return <div>{this.props.bar}</div>;
  }
}

#5 Tooling / Documentation

All of JS's built-in's and DOM documented in your editor

DefinitelyTyped

All major frameworks directly supported

=

A better developer experience when writing JavaScript

#6 Progress on run-time errors

null is not an object

x is undefined

#7 Trends / Community

If you're not using types in js, you will be, probably.

https://2018.stateofjs.com

https://octoverse.github.com/

Flow

TypeScript

People are liking it

GitHub

GitHub

TC39

Eternal wisdom

So, are types necessary?

Ruby, Python, JavaScript


    var isSecure
    var items
    var options
    var _locked
    

duck typing and the DOM

Thanks

@markbrown4

Up Logo