# Aula 3.2 - Interfaces, type aliases e contratos

## Identificação

- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** modelo tipado de tarefas
- **Laboratório:** [`../exemplos/aula-3.2/index.html`](../exemplos/aula-3.2/index.html)

## Introdução

### O que é uma interface no TypeScript?

Uma interface descreve o formato esperado de um objeto:

```ts
interface Task {
  id: number;
  title: string;
  completed: boolean;
}
```

Ela funciona como um contrato estático. Um valor tratado como `Task` precisa
apresentar propriedades compatíveis.

### Interface TypeScript é interface visual?

Não. A palavra “interface” possui significados diferentes:

- interface visual: elementos com os quais o usuário interage;
- interface TypeScript: contrato de estrutura no código;
- interface de API: contrato de comunicação entre sistemas.

Nesta aula, `interface` significa uma declaração de tipo.

### O que é um type alias?

Um alias atribui um nome a qualquer tipo:

```ts
type TaskStatus = "todo" | "doing" | "done";
```

Também pode nomear objetos:

```ts
type Task = {
  id: number;
  title: string;
};
```

Interfaces e aliases se sobrepõem em muitos usos, mas não são idênticos.

### Interface ou type: qual devo usar?

Uma convenção prática:

- `interface` para contratos de objetos que podem ser estendidos;
- `type` para unions, tuplas, primitivas nomeadas e composições;
- consistência da equipe é mais importante que transformar a escolha em disputa.

```ts
type TaskStatus = "todo" | "doing" | "done";

interface Task {
  id: number;
  status: TaskStatus;
}
```

### O contrato existe no navegador?

Não. Interfaces e aliases são removidos na compilação:

```ts
interface Task {
  title: string;
}
```

Não produz objeto ou função no JavaScript final. Por isso, dados de formulário,
JSON e API continuam precisando de validação.

### O que significa `readonly`?

```ts
interface Task {
  readonly id: number;
  title: string;
}
```

TypeScript impede reatribuir `id` por código tipado. Isso não congela o objeto em
ambiente de execução e não torna automaticamente propriedades internas imutáveis.

### Onde isso aparece no projeto?

O laboratório modelará `Task`, `TaskStatus` e `TaskPriority`. Ele validará strings
recebidas do formulário antes de construir um objeto compatível com o contrato.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. explicar interface como contrato estático;
2. criar interfaces e aliases;
3. diferenciar interface visual e interface de tipo;
4. escolher entre `interface` e `type`;
5. declarar propriedades opcionais;
6. utilizar `readonly` conscientemente;
7. estender e compor tipos;
8. compreender tipagem estrutural;
9. reconhecer verificações de propriedades excedentes;
10. modelar entradas, entidades e atualizações separadamente;
11. validar dados antes de afirmar um contrato.

## Pré-requisitos

- Aula 3.1 concluída;
- objetos, funções, arrays e JSON;
- noção de compilação e apagamento de tipos.

## Pergunta orientadora

> Como transformar o formato esperado dos dados em contratos reutilizáveis sem confundir contrato estático com validação real?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| Interfaces e aliases | 25 min |
| Opcionais, readonly e funções | 25 min |
| Extensão e composição | 25 min |
| Modelagem por responsabilidade | 20 min |
| Laboratório | 20 min |
| Revisão | 5 min |

## 1. Declarando uma interface

```ts
interface Student {
  id: number;
  name: string;
  active: boolean;
}
```

Uso:

```ts
const student: Student = {
  id: 1,
  name: "Ana",
  active: true,
};
```

Se faltar uma propriedade obrigatória, TypeScript informa erro.

## 2. Alias de objeto

```ts
type Student = {
  id: number;
  name: string;
  active: boolean;
};
```

Para esse caso simples, o comportamento é muito parecido com a interface.

## 3. Alias de union

```ts
type TaskStatus = "todo" | "doing" | "done";
```

Agora qualquer outro texto é rejeitado:

```ts
const status: TaskStatus = "cancelled";
```

Unions serão aprofundadas na Aula 3.3.

## 4. Propriedade opcional

```ts
interface Task {
  title: string;
  description?: string;
}
```

Um objeto pode omitir `description`:

```ts
const task: Task = {
  title: "Estudar",
};
```

Ao ler:

```ts
task.description?.toUpperCase();
```

O tipo é `string | undefined`.

## 5. Opcional versus `undefined`

```ts
interface OptionalDescription {
  description?: string;
}
```

Permite omitir a chave.

```ts
interface RequiredDescriptionKey {
  description: string | undefined;
}
```

Exige a chave, mesmo que o valor seja `undefined`.

Essa diferença importa em atualizações, serialização e contratos de API.

## 6. Opcional versus `null`

```ts
interface Task {
  completedAt: string | null;
}
```

A chave sempre existe, mas `null` representa ausência intencional.

Escolha conforme o significado do domínio:

- opcional: campo pode não fazer parte daquela representação;
- `null`: campo faz parte do modelo, mas ainda não possui valor;
- string vazia: existe texto, porém sem conteúdo.

## 7. `readonly`

```ts
interface Task {
  readonly id: number;
  title: string;
}

const task: Task = {
  id: 1,
  title: "Estudar",
};

task.id = 2; // erro estático
task.title = "Praticar"; // permitido
```

`readonly` protege a atribuição pelo sistema de tipos.

## 8. `readonly` é raso

```ts
interface Task {
  readonly metadata: {
    source: string;
  };
}

task.metadata.source = "manual";
```

A propriedade `metadata` não pode apontar para outro objeto, mas seu conteúdo ainda
pode ser alterado.

Para estruturas imutáveis:

```ts
interface Task {
  readonly metadata: {
    readonly source: string;
  };
}
```

## 9. Arrays somente leitura

```ts
interface Task {
  readonly tags: readonly string[];
}
```

Isso impede:

```ts
task.tags.push("nova");
```

Podemos criar um novo array:

```ts
const updatedTags = [...task.tags, "nova"];
```

## 10. Métodos em interfaces

```ts
interface TaskRepository {
  findById(id: number): Task | undefined;
  save(task: Task): void;
}
```

A interface descreve o comportamento esperado, não implementa o método.

## 11. Tipos de função

```ts
type TaskFormatter = (task: Task) => string;

const formatTask: TaskFormatter = (task) => {
  return `${task.id} — ${task.title}`;
};
```

O alias pode nomear uma assinatura reutilizável.

## 12. Extensão de interfaces

```ts
interface Entity {
  readonly id: number;
  createdAt: string;
}

interface Task extends Entity {
  title: string;
  status: TaskStatus;
}
```

`Task` recebe as propriedades de `Entity`.

Use extensão quando existe uma relação conceitual clara. Não crie hierarquias
profundas apenas para reduzir linhas.

## 13. Intersections

```ts
type Entity = {
  readonly id: number;
};

type Timestamped = {
  createdAt: string;
};

type Task = Entity & Timestamped & {
  title: string;
};
```

O tipo resultante precisa satisfazer todas as partes.

Intersections não são “mesclar objetos em ambiente de execução”. Elas compõem contratos
estáticos.

## 14. Declaration merging

Interfaces com o mesmo nome no mesmo escopo podem ser combinadas:

```ts
interface Window {
  courseVersion: string;
}
```

Esse recurso ajuda a ampliar tipos de bibliotecas e do ambiente.

Aliases não permitem a mesma redeclaração. Em contratos internos comuns, evite
declarar a mesma interface em vários lugares sem intenção explícita.

## 15. Tipagem estrutural

TypeScript considera a estrutura:

```ts
interface Named {
  name: string;
}

const student = {
  id: 1,
  name: "Ana",
};

function greet(value: Named): string {
  return `Olá, ${value.name}`;
}

greet(student);
```

`student` é aceito porque possui ao menos a estrutura necessária. Não precisa
declarar que “implementa” `Named`.

## 16. Propriedades excedentes

Ao passar um objeto literal diretamente, TypeScript verifica campos inesperados:

```ts
function createTask(input: { title: string }): void {
}

createTask({
  title: "Estudar",
  typo: true,
});
```

Essa verificação ajuda a encontrar erros de digitação.

Uma variável com propriedades adicionais pode ser compatível estruturalmente:

```ts
const input = {
  title: "Estudar",
  source: "manual",
};

createTask(input);
```

Isso não significa que TypeScript remove `source` em ambiente de execução.

## 17. Index signatures

Quando as chaves são dinâmicas:

```ts
interface ErrorMessages {
  [field: string]: string;
}

const errors: ErrorMessages = {
  title: "Título obrigatório",
  estimate: "Estimativa inválida",
};
```

Uma assinatura ampla diz que toda chave string produz aquele tipo. Use com cuidado,
pois ela pode esconder chaves digitadas incorretamente.

## 18. `Record`

```ts
type TaskStatus = "todo" | "doing" | "done";

const statusLabels: Record<TaskStatus, string> = {
  todo: "A fazer",
  doing: "Em andamento",
  done: "Concluída",
};
```

`Record` exige uma entrada para cada status. Ele é um utility type genérico.

## 19. Separar entrada e entidade

O formulário ainda não possui identificador ou datas:

```ts
interface CreateTaskInput {
  title: string;
  status: TaskStatus;
  priority: TaskPriority;
  estimate: number;
  description?: string;
}
```

A entidade completa:

```ts
interface Task extends CreateTaskInput {
  readonly id: number;
  readonly createdAt: string;
  completedAt: string | null;
}
```

Não force a interface do formulário a inventar campos que pertencem à criação.

## 20. Tipo de atualização

Uma atualização não precisa repetir todos os campos:

```ts
type UpdateTaskInput = {
  title?: string;
  status?: TaskStatus;
  priority?: TaskPriority;
  estimate?: number;
  description?: string;
};
```

Mais adiante poderemos usar:

```ts
type UpdateTaskInput = Partial<CreateTaskInput>;
```

Porém regras específicas podem impedir que todo campo seja opcional ou atualizável.

## 21. `Pick` e `Omit`

```ts
type TaskSummary = Pick<
  Task,
  "id" | "title" | "status"
>;
```

```ts
type EditableTask = Omit<
  Task,
  "id" | "createdAt"
>;
```

Esses tipos derivam contratos existentes e reduzem duplicação. Não os use se o
resultado ficar mais difícil de compreender que uma declaração direta.

## 22. `satisfies`

```ts
const statusLabels = {
  todo: "A fazer",
  doing: "Em andamento",
  done: "Concluída",
} satisfies Record<TaskStatus, string>;
```

`satisfies` verifica compatibilidade sem substituir o tipo inferido do valor. É útil
para configurações completas.

## 23. Contrato não valida formulário

Este código é inseguro:

```ts
const status = formData.get("status") as TaskStatus;
```

A assertion não confirma o texto. Valide:

```ts
function isTaskStatus(value: unknown): value is TaskStatus {
  return (
    typeof value === "string" &&
    ["todo", "doing", "done"].includes(value)
  );
}
```

Depois da verificação, TypeScript estreita o tipo.

## 24. Modelo do laboratório

```ts
type TaskStatus = "todo" | "doing" | "done";
type TaskPriority = "low" | "planned" | "high" | "critical";

interface Task {
  readonly id: number;
  title: string;
  status: TaskStatus;
  priority: TaskPriority;
  estimate: number;
  description?: string;
}
```

O JavaScript gerado não contém essas declarações. As funções de validação continuam
no ambiente de execução.

## 25. Laboratório guiado

Abra o [modelador tipado](../exemplos/aula-3.2/index.html).

### Etapa 1 — Preencha o contrato

Informe título, status, prioridade, estimativa e descrição opcional.

### Etapa 2 — Teste campo opcional

Crie uma tarefa com descrição vazia. A propriedade será omitida. Depois, informe uma
descrição e compare o objeto.

### Etapa 3 — Teste valores

Use estimativa inválida e título curto. A interface deve impedir a criação antes de
afirmar que existe uma `Task`.

### Etapa 4 — Compare TypeScript e JavaScript

Localize em `src/app.ts`:

- `TaskStatus`;
- `TaskPriority`;
- `Task`;
- `CreateTaskInput`;
- validações de ambiente de execução.

Abra `dist/app.js` e confirme o apagamento dos tipos.

## 26. Erros comuns

### Criar uma interface gigantesca

Contratos que servem formulário, entidade, resposta e atualização ao mesmo tempo
acumulam opcionais e perdem significado.

### Usar `readonly` como segurança

É proteção estática contra atribuição, não criptografia ou autorização.

### Usar assertion para “resolver”

`as Task` não cria propriedades nem valida dados.

### Confundir opcional e anulável

Ausência da chave e valor `null` podem ter contratos de API diferentes.

### Estender sem relação conceitual

Herança de tipos deve comunicar o domínio, não apenas evitar repetição.

## 27. Boas práticas

- nomeie contratos pelo papel;
- separe entrada, entidade, resumo e atualização;
- use aliases para unions;
- use interfaces para objetos extensíveis;
- mantenha propriedades obrigatórias quando o domínio exige;
- use `readonly` para identidade e dados imutáveis;
- derive tipos sem esconder regras;
- prefira `unknown` para entrada externa;
- valide antes de afirmar o contrato;
- documente diferenças entre ausência e `null`.

## 28. Exercícios

### Exercício 1 — Interface

Crie `Student` com `readonly id`, `name` e `email?`.

### Exercício 2 — Alias

Crie `EnrollmentStatus` com três valores literais.

### Exercício 3 — Extensão

Crie `Entity` e estenda em `Course`.

### Exercício 4 — Função

Crie o tipo de uma função que recebe `Task` e retorna string.

### Exercício 5 — Entrada

Modele `CreateTaskInput` sem os campos gerados pelo sistema.

## 29. Desafio

Modele:

- entidade completa;
- entrada de criação;
- entrada de atualização;
- resumo para lista;
- mapa de rótulos com `Record`;
- validador de ambiente de execução para status e prioridade.

Explique cada campo opcional ou anulável.

## 30. Lista de verificação de conclusão

- [ ] Sei explicar interface TypeScript.
- [ ] Sei criar aliases de union.
- [ ] Diferencio opcional e `undefined`.
- [ ] Diferencio ausente e `null`.
- [ ] Entendo que `readonly` é estático e raso.
- [ ] Consigo estender e compor contratos.
- [ ] Entendo tipagem estrutural.
- [ ] Sei separar entrada e entidade.
- [ ] Não uso `as` como validação.
- [ ] Valido dados de formulário em ambiente de execução.

## 31. Critérios de avaliação

| Critério | Pontos |
|---|---:|
| Modelagem da entidade | 25 |
| Separação dos contratos | 20 |
| Opcionais e readonly coerentes | 15 |
| Composição e reutilização | 15 |
| Validação em ambiente de execução | 20 |
| Explicação das decisões | 5 |
| **Total** | **100** |

## Resumo

Nesta aula, aprendemos que:

- interfaces e aliases nomeiam contratos;
- interfaces não são elementos visuais;
- aliases representam objetos, unions e outros tipos;
- opcionais, `null` e `undefined` têm significados diferentes;
- `readonly` é uma proteção estática e rasa;
- TypeScript usa tipagem estrutural;
- contratos de entrada e entidade devem ser separados;
- tipos apagados não validam dados externos.

## Próxima aula

Na Aula 3.3, aprofundaremos unions, narrowing, type guards, discriminated unions e
generics para representar diferentes estados com segurança.
