Javascript com tipos

Por que usar tipos no seu projeto Javascript

Javascript com tipos?

Centenas de linguagens foram desenhadas ou adaptadas para compilar para Javascript

Vamos focar nas duas que mantém a sintaxe original

Por que tipos estáticos?

Mais tipos, menos bugs

Mais tipos, menos testes

(ainda que um não substitua o outro)

Tipos são uma forma de documentação

Que funções esse módulo exporta?

Que parâmetros essa função recebe?

O que essa função retorna?

Contrato entre diferentes partes de uma aplicação

Exemplo: schema do GraphQL gerando tipos em Flow ou Typescript

Refatorar com confiança

E, se houver integração com ferramentas, de forma automática

As garotas da noite

Mantido pela Microsoft

Superset de Javascript

Prover funcionalidades do Javascript ainda planejadas hoje (ex: decorators, private instance methods, class fields) e não planejadas (Enum)

Transpilado

class Pessoa {
    constructor(public cumprimento: string) {}

    cumprimenta() {
        return `<h1>${this.cumprimento}</h1>`;
    }
};

var pessoa = new Pessoa("Ata");
pessoa.cumprimenta();
var Pessoa = /** @class */ (function () {
    function Pessoa(cumprimento) {
        this.cumprimento = cumprimento;
    }

    Pessoa.prototype.cumprimenta = function () {
        return "<h1>" + this.cumprimento + "</h1>";
    };
    return Pessoa;
}());

var pessoa = new Pessoa("Ata");
pessoa.cumprimenta();

http://www.typescriptlang.org/play/

Tipagem estrutural

interface Square {
    height: number;
    width: number;
}

interface Rect {
    height: number;
    width: number;
}

let rect: Rect;
const square = {
    width: 4,
    height: 4
};

rect = square; // Ok

Mantido pelo Facebook

Uma extensão do Javascript

Não uma linguagem a parte

Apenas retira as anotações

/* @flow */

class Pessoa {
  cumprimento: string;

  constructor(cumprimento: string) {
    this.cumprimento = cumprimento;
  }

  cumprimenta() {
    return `<h1>${this.cumprimento}</h1>`;
  }
}

const pessoa = new Pessoa('Ata');
pessoa.cumprimenta();
class Pessoa {
  constructor(cumprimento: string) {
    this.cumprimento = cumprimento;
  }

  cumprimenta() {
    return `<h1>${this.cumprimento}</h1>`;
  }
}

const pessoa = new Pessoa('Ata');
pessoa.cumprimenta();

https://flow.org/try/

Objetivos

  • Ser preciso:
    • Reportar todos os erros que possam ser detectados, sem falsos negativos
  • Ser rápido:
    • Não quebrar o ciclo do desenvolvedor de editar-atualizar

Inferência > anotações

Tipagem estrutural

(com tipos nominais também)

Objetivos em comum

Ser apenas Javascript™ com tipos estáticos

Adoção gradual com benefícios imediatos

Ser rápido, pra não travar o ciclo de edição e feedback dos desenvolvedores

Funcionalidades exclusivas

Non-null assertion

Typescript

const body = document.body; // HTMLElement | undefined

body.querySelector("nav"); // Erro
body!.querySelector("nav"); // Sem erro

Tipos opacos (ou nominais)

Flow

opaque type CPF = string;
opaque type RG = string;

function validateCpf(cpf: string): CPF | null {
    if (isCpfValid(cpf)) return cpf;
    else return null;
}

function validateRg(rg: string): RG | null { /*...*/ }

let myRg = validateRg(res.rg);
let myCpf = validateCpf(res.cpf);

myCpf = myRg; // Erro
myRg = myCpf; // Erro

Tipos condicionais e inferidos

Typescript

type Exclude<T, U> = T extends U ? never : T;
type NumberAlias = Exclude<string | number, number>;

type ReturnType<T extends Function> = T extends (...args: any[]) => infer R ? R : never;
type StringAlias = ReturnType<() => string>;

Tipo Existencial

Flow

type Entity = "Contact" | "Lawsuit";
type Map<K, V> = { [K]: V }

export const humanizeEntity: Map<Entity, *> = {
    Contact: "Contato",
    Lawsuit: "Processo",
};

Type guard

Typescript

interface Entity<Type extends string> {
    type: Type;
    name: string;
}

interface Organization extends Entity<"org"> {
    location: string;
}

function isEntityOrganization(
    entity: Entity<any>
): entity is Organization {
    return entity.type === "org";
}

const entity: Entity<any> = { /*...*/ };

if (isEntityOrganization(entity) {
    entity.location; // O tipo agora é Organization
}

Outras características e funcionalidades

Refatoração

  • Exemplo: renomear variáveis e tipos
  • É um foco do time do Typescript
  • Integração de editores de texto e afins com Flow ainda é fraca

Geração de tipos por spec

// schema.gql

type Organization {
  title: String
  id: Int!
}
// types.ts

export interface Organization {
  title?: string | null;
  id: number;
}

export type Resolver<Result, Parent = any, Context = any, Args = any> = (
  parent: Parent,
  args: Args,
  context: Context,
  info: GraphQLResolveInfo
) => Promise<Result> | Result;

export namespace OrganizationResolvers {
    /* ... */
}

Ecossistema JS:

ESLint, Babel e afins

  • Typescript exige ferramentas a parte, enquanto Flow é apenas Javascript
    • Isso pode mudar com a implementação de Typescript do Babel ♥

Tipo de bibliotecas de terceiros

  • Mais bibliotecas tem declaração de tipos em Typescript:
    • flow-typed: ~500 bibliotecas
    • definitely-typed: 5064 bibliotecas
  • O processo de instalação e consumo é mais ergonômico em Typescript
    • Declarações de tipo vem na própria lib, se seu package.json tiver o campo 'types'; ou o tipo é declarado a parte em pacote @types/lib
  • Flow historicamente integra melhor com React (ex: defaultProps), mas Typescript implementou as features que faltavam

Plugins do Typescript

  • Plugins permitem:
    • Usar outra gramática dentro de tagged template literals:
      • Exemplos de onde isso é útil:
        • Relay: Relay.QL`fragment on Root { ... }`
        • CSS in JS: styled.div` background-color: var(--primary); `
      • Essas gramáticas permitem:
        • Fazer highlight de sintaxe (e de erros de sintaxe)
        • Autocomplete
    • Criar novos refactorings para editores de texto
    • Gerar valores a partir de tipos

Ergonomia / DX

  • Ambos suportam tipos a partir de comentários, exigindo pouco esforço para uma adoção gradual
    • Mas tipar com comentários duplica código, é menos legível e exige mais esforço
  • Flow exige apenas um novo plugin no Babel (mas Typescript deve chegar lá também)
  • Ambos tendem a consumir muita memória com o tempo

Inferência favorece agilidade

Flow

Como decidir?

Flow’s lighter-weight approach may suit dynamic-typing advocates better, whereas TypeScript’s DefinitelyTyped library and approach may perhaps be more appealing to strong-typing advocates coming from other languages.

O quanto tipar?

Nós já falamos disso

Mas não disso

É preciso achar o sweet spot

O seu sweet spot

Happy hacking!

Javascript com tipos

By Victor Magalhães

Javascript com tipos

Por que usar tipos no seu projeto Javascript

  • 677