Menu

extends em TypeScript: estender uma interface ou um type

A palavra-chave extends cria um tipo a partir de outro. Veja como estender uma interface (uma ou várias), estender um type alias com &, sobrescrever o tipo de uma propriedade, trocar propriedades com Omit e o que extends significa em classes, restrições de generics e conditional types.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

interface Child extends Parent cria uma nova interface com todos os membros de Parent mais os próprios. Valores do tipo filho podem ser usados em qualquer lugar em que o pai é esperado, porque têm todas as propriedades dele.

A mesma palavra-chave aparece em quatro lugares no TypeScript, com significados relacionados, mas diferentes:

OndeExemploSignificado
Interfaceinterface Dog extends Animal {}herdar os membros do pai
Classeclass Dog extends Animal {}herdar campos e implementações de métodos (JavaScript em tempo de execução)
Type parameter genérico<T extends Animal>restrição: o argumento de tipo precisa poder ser atribuído a Animal
Conditional typeT extends string ? A : Bteste: T pode ser atribuído a string?

Estendendo várias interfaces

Liste vários pais depois de extends, separados por vírgulas. O resultado tem os membros de todos eles:

interface Identified {
  id: number;
}

interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface Post extends Identified, Timestamped {
  title: string;
}

// Post requires id, createdAt, updatedAt and title

Se dois pais declaram a mesma propriedade com tipos que não são idênticos, a filha gera o erro TS2320 (Interface 'Post' cannot simultaneously extend types 'A' and 'B'), a menos que ela redeclare essa propriedade com um tipo atribuível aos dois.

Estendendo um type alias

Um alias type não tem cláusula extends. Você parte dele com uma intersection, &, que combina os membros dos dois lados. Uma interface também pode estender diretamente um type alias de objeto, e um type alias pode fazer intersection com uma interface: os dois tipos se misturam livremente.

Os dois caminhos se comportam igual até as propriedades entrarem em conflito: o extends aponta uma propriedade conflitante na declaração, enquanto o & a transforma em never em silêncio. A comparação está na página sobre interface vs type, e os detalhes do & na página sobre intersection types.

Sobrescrevendo tipos de propriedades

Uma interface filha pode redeclarar uma propriedade do pai, mas só com um tipo atribuível ao do pai. Estreitar string para um literal, ou remover null de uma union, é permitido. Trocar o tipo por algo sem relação não é:

index.ts(13,11): error TS2430: Interface 'Label' incorrectly extends interface 'Shape'.
  Types of property 'kind' are incompatible.
    Type 'number' is not assignable to type 'string'.

A regra existe para que um Label ainda possa ser usado em todo lugar em que um Shape é esperado. Se kind pudesse ser um número, código escrito para Shape que chama shape.kind.toUpperCase() quebraria.

Trocando propriedades com Omit

Quando você realmente precisa de outro tipo para uma propriedade herdada, remova-a do pai antes com Omit e declare a nova. Um caso comum é converter dados de uma API, em que ids e datas chegam como strings:

Omit<ApiUser, "id" | "createdAt"> é { name: string }, então os novos id e createdAt não conflitam com nada. Pick, Partial e os outros utility types podem ser estendidos do mesmo jeito.

extends com classes

class Dog extends Animal é herança de classes do JavaScript e, diferente do extends de interfaces, existe em tempo de execução: Dog recebe o construtor, os campos e as implementações de métodos de Animal, e new Dog(...) instanceof Animal é true. O TypeScript acrescenta verificações de tipo por cima, como a palavra-chave override:

Uma classe só pode fazer extends de uma classe, mas pode fazer implements de qualquer número de interfaces. implements não copia nada: só verifica se a classe declara o que a interface exige. super, membros protected e override estão em herança.

extends em generics e conditional types

Dentro dos sinais de menor e maior, extends é uma restrição. <T extends { length: number }> aceita qualquer argumento de tipo que tenha um length numérico e deixa o corpo da função usar .length. Em um conditional type, T extends U ? X : Y pergunta se T pode ser atribuído a U.

Nos dois lugares, extends significa "pode ser atribuído a", a mesma relação que a forma de interface verifica. As restrições são tratadas a fundo em generic constraints, e os testes no nível dos tipos em conditional types.

Perguntas frequentes

Como estender uma interface no TypeScript?

Escreva interface Child extends Parent { ... }. A filha tem todas as propriedades do pai mais as que ela declara. Para estender várias interfaces de uma vez, separe-as com vírgulas: interface C extends A, B { ... }.

Como estender um type alias no TypeScript?

Um type alias não pode usar extends; combine os tipos com uma intersection: type Admin = User & { permissions: string[] }. Uma interface também pode estender diretamente um type alias de objeto: interface Admin extends User { permissions: string[] }.

Posso sobrescrever o tipo de uma propriedade ao estender uma interface?

Só com um tipo compatível e mais estreito: kind: "circle" pode substituir kind: string. Um tipo diferente, como number para uma propriedade string, gera o erro TS2430 (Interface 'X' incorrectly extends interface 'Y'). Para trocar uma propriedade por um tipo sem relação, remova-a antes: interface User extends Omit<ApiUser, "id"> { id: number }.

Qual é a diferença entre extends e implements?

extends herda: uma interface recebe os membros do pai, e uma classe recebe os campos e as implementações de métodos da classe pai. implements só verifica: uma classe que implementa uma interface precisa declarar ela mesma cada membro, e não ganha nada da interface.

O que extends significa em um generic como <T extends string>?

É uma restrição, não herança. T extends string significa que o argumento de tipo precisa poder ser atribuído a string, então dentro da função você pode usar métodos de string em T. Em um conditional type, T extends U ? X : Y é um teste: se T pode ser atribuído a U, o resultado é X; senão, Y.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR