Convincing a Type Sceptic
Flow
Flow is a static type checker for JavaScript.
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
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.
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