Menu

Modificadores de acesso em TypeScript: public, private, protected

O TypeScript tem três modificadores de acesso, public, private e protected, além do readonly. Veja o que cada um permite, por que o private do TypeScript é uma verificação de tempo de compilação enquanto os campos #private do JavaScript são aplicados em tempo de execução, e qual escolher.

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

O TypeScript tem três modificadores de acesso para membros de classe: public (o padrão), protected e private. Eles controlam onde um membro pode ser usado, e o compilador aponta qualquer acesso feito do lugar errado.

A última linha é um erro de compilação, mas veja o que ela imprimiu quando rodou mesmo assim: 123-45-6789. Esse é o fato mais importante desta página, e a seção sobre private o explica.

O que cada modificador permite

ModificadorDentro da classeEm uma subclasseDe foraAplicado em tempo de execução
public (padrão)simsimsimnada a aplicar
protectedsimsimnãonão
privatesimnãonãonão
#name (JavaScript)simnãonãosim
readonlyleitura, e escrita no construtorleituraleituranão

Os modificadores funcionam em campos, métodos, getters e setters, construtores e parameter properties (constructor(private id: string)). Escrever public é opcional, e muitas bases de código o omitem.

private é uma verificação de tempo de compilação

O TypeScript apaga o private junto com o resto dos tipos. A classe compilada tem uma propriedade comum, então tudo o que não passa pelo verificador de tipos a enxerga: quem chama em JavaScript puro, JSON.stringify, Object.keys e até a notação de colchetes do próprio TypeScript, permitida de propósito como válvula de escape.

Isso está certo para o que o private se propõe: dizer a outros desenvolvedores (e ao seu editor) que um membro é um detalhe de implementação. Ele não é uma barreira de segurança, e um campo private vai acabar em logs e em respostas JSON, a menos que você mesmo o remova.

Campos #private: aplicados em tempo de execução

O JavaScript tem os próprios campos privados, escritos com #. A engine os aplica: fora da classe, obj.#field nem é sintaxe válida, e o campo não aparece em Object.keys, JSON.stringify nem console.log.

Escrever s.#token fora da classe gera o erro TS18013 (Property '#token' is not accessible outside class 'Session' because it has a private identifier) e, diferente do caso do private, também não há como contornar isso em tempo de execução. Veja campos privados em JavaScript para as regras de tempo de execução.

private vs #private: qual usar

private x#x
Verificado poro compiladora engine JavaScript
Visível para Object.keys / JSON.stringifysimnão
Acesso com colchetes obj["x"]permitidoimpossível
Uma subclasse pode declarar o próprio xnão, há conflitosim, cada classe tem o próprio #x
Copiado por um spread { ...obj }simnão
Sintaxe em métodosprivate helper()#helper()

Use #private quando os dados precisam continuar privados em tempo de execução (tokens, estado interno que os usuários de uma biblioteca não podem mexer) ou quando você quer que o JSON.stringify os deixe de fora. Use private quando o objetivo é só uma API pública limpa, quando um framework precisa ler o campo por reflexão ou para seguir o estilo existente de uma base de código. Não combine os dois: private #x gera o erro TS18010 (An accessibility modifier cannot be used with a private identifier).

protected e subclasses

Um membro protected fica disponível dentro da classe e em qualquer classe que a estenda, mas não em instâncias vistas de fora.

Uma regra surpreende as pessoas. Dentro de Polygon, você pode ler sides no this ou em outro Polygon, mas não em um Shape simples recebido como parâmetro: other.sides com other: Shape gera o erro TS2446 (Property 'sides' is protected and only accessible through an instance of class 'Polygon'. This is an instance of class 'Shape'.). Uma subclasse só pode alcançar membros protected de objetos que pertencem ao seu próprio ramo da hierarquia.

Uma subclasse pode tornar público um membro protected redeclarando-o, mas não pode tornar protected ou private um membro público. Redeclare-o com um inicializador (public override sides = 6) ou só como tipo (declare public sides: number). Um public sides: number; sem mais nada é rejeitado com TS2564 e TS2612, porque com os class fields modernos ele reiniciaria o valor herdado para undefined depois que super() retornasse.

readonly

O readonly bloqueia a reatribuição depois da construção. O campo pode ser definido na declaração ou no construtor; qualquer atribuição posterior gera o erro TS2540.

Dois limites aparecem aqui. O readonly é raso: o array não pode ser substituído, mas o conteúdo dele pode mudar (tipe como readonly string[] para bloquear o push). E, como o private, ele desaparece em tempo de execução, então a atribuição que o compilador rejeitou rodou mesmo assim. Para um valor que não pode mudar em tempo de execução, use Object.freeze ou um getter sem setter. A página sobre readonly trata de Readonly<T> e arrays readonly.

O readonly se combina com os modificadores de acesso: private readonly cache = new Map<string, number>() é um padrão comum para um campo interno que nunca é reatribuído.

Erros comuns

  • Tratar o private como segurança. Ele some em tempo de execução. Use #field para dados que precisam ficar escondidos, e nunca mande um objeto com segredos direto para o JSON.stringify.
  • Escrever public em todo lugar. É o padrão; acrescentá-lo não muda nada.
  • Usar protected para tudo o que é "interno". Se nenhuma subclasse precisa, o private mantém a superfície menor.
  • Esperar que o readonly congele dados aninhados. Ele só impede a reatribuição da própria propriedade.

Perguntas frequentes

Quais são os modificadores de acesso do TypeScript?

public (o padrão: acessível em todo lugar), protected (dentro da classe e das subclasses) e private (só dentro da classe). readonly é um modificador à parte que bloqueia a reatribuição depois do construtor e se combina com qualquer um dos três.

Qual é a diferença entre private e #private no TypeScript?

O private é verificado só pelo compilador; o JavaScript gerado tem uma propriedade comum, então obj["secret"], JSON.stringify e Object.keys continuam enxergando-a. #secret é um campo privado do JavaScript: o runtime o aplica, e código fora da classe não consegue lê-lo de jeito nenhum.

O private do TypeScript é realmente privado?

Só em tempo de compilação. Depois da compilação, o campo é uma propriedade normal que qualquer código JavaScript pode ler. O TypeScript até permite acesso com colchetes (obj["field"]) a membros privados como válvula de escape. Use #field quando a privacidade precisa valer em tempo de execução.

Qual é a diferença entre protected e private no TypeScript?

Um membro private só é visível dentro da classe que o declara. Um membro protected também é visível dentro das subclasses. Nenhum dos dois pode ser acessado em uma instância de fora da hierarquia de classes.

Propriedades readonly podem ser alteradas no TypeScript?

Uma propriedade readonly pode ser atribuída na declaração ou no construtor, e em nenhum outro lugar (senão, TS2540). Ela é rasa: um readonly tags: string[] não pode ser substituído, mas tags.push() continua funcionando. Use readonly string[] para bloquear isso também.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR