Um aspecto do desenvolvimento de JavaScript com o qual muitos desenvolvedores lutam é lidar com valores opcionais. Quais são as melhores estratégias para minimizar erros causados por valores que podem ser null
, undefined
ou de outra forma não inicializados em tempo de execução?
Alguns idiomas possuem recursos internos para essas circunstâncias. Em algumas linguagens tipadas estaticamente, você pode dizer que null
e undefined
são valores ilegais e deixe sua linguagem de programação lançar um TypeError em tempo de compilação , mas mesmo nessas linguagens, isso não pode impedir que entradas nulas fluam para o programa em tempo de execução.
Para lidar melhor com esse problema, precisamos entender de onde esses valores podem vir. Aqui são algumas das fontes mais comuns:
- entrada do usuário
- banco de dados / registros de rede
- estado não inicializado
- funções que podem não retorne nada
Quando você está lidando com a entrada do usuário, a validação é a primeira e a melhor linha de defesa. Muitas vezes, confio nos validadores de esquema para ajudar nesse trabalho. Por exemplo, verifique a reação -jsonschema-form.
Registros de hidratação da entrada
Eu sempre passo entradas que recebo da rede, banco de dados ou entrada do usuário por meio de uma função de hidratação. Por exemplo, vou usar redux criadores de ação que podem lidar com
valores para hidratar os registros do usuário:
const setUser = ({ name = "Anonymous", avatar = "anon.png" } = {}) => ({
type: setUser.type,
payload: {
name,
avatar
}
});
setUser.type = "userReducer/setUser";
Às vezes, você precisará exibir coisas diferentes, dependendo do estado atual dos dados. Se for possível exibir uma página antes de todos os dados serem inicializados, você pode se encontrar nessa situação. Por exemplo, ao exibir saldos de dinheiro para um usuário, você pode acidentalmente exibir um saldo de $ 0 antes do carregamento dos dados Já vi isso incomodar os usuários várias vezes. Você pode criar tipos de dados personalizados que geram resultados diferentes com base no estado atual:
O código acima é uma máquina de estado que torna impossível exibir estados inválidos. Quando você cria o saldo pela primeira vez, ele será definido para um estado uninitialized
. Se você tentar exibir um saldo enquanto o estado é uninitialized
, você sempre obterá um valor de espaço reservado (“--
“).
Para mudar isso, você deve definir explicitamente um valor chamando o método .set
ou o atalho setBalance
definimos abaixo da createBalance
fábrica.
O próprio estado é encapsulado para protegê-lo de interferências externas para garantir que outras funções não possam capturá-lo e configurá-lo para um estado inválido.
Observação: se você está se perguntando por que estamos usando strings em vez de números para isso, é porque represento tipos de dinheiro com strings de números grandes com muita precisão decimal para evitar erros de arredondamento e representar com precisão os valores para transações de criptomoeda, que podem ter precisão decimal arbitrária significativa.
Se você ‘ Ao usar a arquitetura Redux ou Redux, você pode declarar máquinas de estado com Redu x-DSM.
Evite criar valores nulos e indefinidos
Em suas próprias funções, você pode evitar criar null
ou undefined
valores para começar. Existem algumas maneiras de fazer isso embutido no JavaScript que vêm à mente. Veja abaixo.
Evite nulos
Eu nunca crio explicitamente null
valores em JavaScript, porque nunca vi o sentido de ter dois valores diferentes valores primitivos que significam essencialmente “este valor não existe”.
Desde 2015, o JavaScript oferece suporte a valores padrão que são preenchidos quando você não fornece um valor para o argumento ou propriedade em questão. Esses padrões não funcionam para valores null
. Isso geralmente é um bug, na minha experiência. Para evitar essa armadilha, não use null
em JavaScript.
Se você deseja casos especiais para valores não inicializados ou vazios, as máquinas de estado são uma aposta melhor. Veja acima.
Novos recursos de JavaScript
Existem alguns recursos que podem ajudá-lo a lidar com null
ou undefined
valores. Ambas são propostas de estágio 3 no momento em que este livro foi escrito, mas se você estiver lendo do futuro, poderá ser capaz de usá-las.
No momento desta redação, o encadeamento opcional é uma proposta de estágio 3. Funciona assim:
const foo = {};
// console.log(foo.bar.baz); // throws error
console.log(foo.bar?.baz) // undefined
Operador de coalescência nula
Também uma proposta de estágio 3 a ser adicionada à especificação, “operador de coalescência nula ”É basicamente uma maneira elegante de dizer” operador de valor substituto “. Se o valor à esquerda for undefined
ou null
, ele avalia como o valor à direita.Funciona assim:
Asynchronous Either with Promises
Se uma função pode não retornar com um valor, pode ser uma boa idéia envolvê-la em um Either. Na programação funcional, a Mônada Either é um tipo de dados abstrato especial que permite anexar dois caminhos de código diferentes: um caminho de sucesso ou um caminho de falha. JavaScript tem um tipo de dados monadish assíncrono integrado denominado Promise. Você pode usá-lo para fazer ramificações de erro declarativas para valores indefinidos:
Você poderia escrever uma versão síncrona disso se quiser, mas não precisei muito disso. Vou deixar isso como um exercício para você. Se você tiver um bom aterramento em functores e mônadas, o processo será mais fácil. Se isso parece intimidante, não se preocupe com isso. Basta usar promessas. Eles são integrados e funcionam bem na maioria das vezes.
Arrays para Maybes
Os arrays implementam um método map
que leva uma função que é aplicada a cada elemento da matriz. Se a matriz estiver vazia, a função nunca será chamada. Em outras palavras, Arrays em JavaScript podem preencher o papel de Maybes de linguagens como Haskell.
O que é um Maybe?
Um Maybe é um tipo de dado abstrato especial que encapsula um valor opcional . O tipo de dados assume duas formas:
- Apenas – Um Talvez que contém um valor
- Nada – um Talvez sem valor
Aqui está a essência da ideia:
Este é apenas um exemplo para demonstrar o conceito. Você poderia construir uma biblioteca inteira de funções úteis em torno de maybes, implementando outras operações como flatMap
e flat
(por exemplo, para evitar Just(Just(value))
ao compor várias funções de retorno Talvez). Mas JavaScript já tem um tipo de dados que implementa muitos desses recursos prontos para uso, então geralmente procuro por isso: O Array.
Se você quiser criar uma função que pode ou pode não produzir um resultado (principalmente se pode haver mais de um resultado), você pode ter um ótimo caso de uso para retornar uma matriz.
Acho que map
não será chamado em uma lista vazia, muito útil para evitar valores null
e undefined
, mas lembre-se, se a matriz contém valores null
e undefined
, ele chamará a função com esses valores, portanto, se a função que você está executando pudesse produzir null
ou undefined
, você “precisará filtrá-los de sua matriz retornada, conforme demonstrado acima. Isso pode ter o efeito de alterar o comprimento de a coleção.
Em Haskell, há uma função maybe
que (como ) aplica uma função a um valor. Mas o valor é opcional e encapsulado em um Maybe
. Podemos usar o tipo de dados JavaScript “s Array
para fazer essencialmente a mesma coisa:
maybe
usa um valor substituto , em seguida, uma função para mapear sobre a matriz talvez, depois uma matriz talvez (uma matriz contendo um valor ou nada) e retorna o resultado da aplicação da função ao conteúdo da matriz ou o valor de fallback se a matriz for vazio.
Por conveniência, também defini uma função toMaybeArray
e selecionei a maybe
função para fazer é mais óbvio para esta demonstração.
Se você gostaria de fazer algo assim no código de produção, criei uma unidade de biblioteca de código aberto testada para facilitar. É chamado Maybearray. A vantagem do Maybearray sobre outras bibliotecas JavaScript Maybe é que ele usa arrays nativos de JavaScript para representar valores, então você não precisa dar a eles nenhum tratamento especial ou fazer qualquer coisa especial para converter para frente e para trás. Quando você encontrar matrizes Maybe em sua depuração, você não precisa perguntar, “o que é esse tipo estranho ?!” É apenas uma matriz de um valor ou uma matriz vazia e você já viu um milhão de vezes antes.
Próximas etapas
Há muito mais conteúdo em EricElliottJS.com, incluindo muito de vídeos, exercícios, screencasts gravados e dicas rápidas. Se você não for membro, agora é uma ótima hora para ver o que você está perdendo!