How to deal with Undefined (or null) union in Typescript?

Asked

Viewed 140 times

4

I am implementing this method below but it is giving me this error:

Type 'Task | Undefined' is not Assignable to type 'Task'. Type 'Undefined' is not Assignable to type 'Task'.

I can’t fix it. The code is this:

function buscarPorId(id: number): Tarefa {
  const tarefas: Tarefa [] = this.listarTodos();
  return tarefas.find(tarefa => tarefa.id === id);
}

Note that the function return type is Tarefa.

1 answer

6

This is happening because the kind of find is always the union of type of array elements and undefined.

So, for example, if you have an array of the type Array<number> and invoke find, the type of return will be number | undefined. And this union (with undefined) is indeed necessary, since, as stated in language specification, Array.prototype.find should return undefined if the element you are looking for does not exist within the array - which, let’s face it, is a completely plausible scenario of happening.

Learn more about type unions in Typescript.

So you have some options...

Set the function return to possibly undefined

For this, just make the type of return as a union of the desired type and undefined.

In your case, the type of return should be Tarefa | undefined. Thus:

function buscarPorId(id: number): Tarefa | undefined {
  const tarefas: Tarefa [] = this.listarTodos();
  return tarefas.find(tarefa => tarefa.id === id);
}

Return a value fallback of the desired type in case the search fails

In this case you can create an instance of Tarefa fallback if the search was not successful. Something like this:

function buscarPorId(id: number): Tarefa {
  const tarefas: Tarefa [] = this.listarTodos();
  const encontrada = tarefas.find(tarefa => tarefa.id === id);

  // Caso não tenha sido encontrada...
  if (!encontrada) {
    return new Tarefa(...); // Informações de fallback
  }

  return encontrada;
}

But in this case this doesn’t seem like a very alternative clever, since it would be "creating" a task which the user did not explicitly create. But it is a valid possibility in other cases and that’s why I put it here.

Make an exception

You can make an exception if no item has been found. It may seem a little absurd (in this case it is, but in some others it may make more sense). Just remember that the caller should be aware of the possible launch to not break the application.

Thus:

function buscarPorId(id: number): Tarefa {
  const tarefas: Tarefa [] = this.listarTodos();
  const encontrada = tarefas.find(tarefa => tarefa.id === id);

  // Caso não tenha sido encontrada...
  if (!encontrada) {
    throw new Error('Nenhuma tarefa foi encontrada com esse parâmetro de busca.');
  }

  return encontrada;
}

For more details on exceptions in general, read this excellent response.


Completion

In short, I think it’s worth returning undefined (the first alternative I presented), since Typescript will compel the caller to deal with the possible value undefined, just as we were forced to deal with the possible undefined returned by Array.prototype.find.

Remember that the option strictNullChecks shall be empowered to ensure that security related to null or undefined be guaranteed. Interestingly, the example of the find is used to explain this option in such documentation.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.