What is the best way to efficiently filter this list of Outcome data generated by neverthrow?

I am working with an array of Results coming from the neverthrow library. My goal is to check if there are any errors in the array and if so, terminate my function. However, the challenge arises when there are no errors present, as I then want to destructure the array into the corresponding Ok types since it's guaranteed that an Err cannot exist at that point.

Let me illustrate the issue with an example:

import { Err, ok, Result } from "neverthrow";

function resultIsError(
  result: Result<unknown, Error>
): result is Err<unknown, Error> {
  return result.isErr();
}

function doSomething() {
  const results = [
    ok(true) as Result<boolean, Error>,
    ok("foobar") as Result<string, Error>,
    ok({ foo: "bar" }) as Result<{ foo: string }, Error>
  ];

  const resultErrors = results.filter(resultIsError);

  if (resultErrors.length > 0) {
    return resultErrors;
  }

  const [someBool, someString, someObj] = results;

  const someBoolValue: boolean = someBool.value;
  const someStringValue: string = someString.value;
  const someObjValue: { foo: string } = someObj.value;
}

The problem surfaces when attempting to access .value, triggering this error message:

Property 'value' does not exist on type 'Err<boolean, Error>'

It seems that Typescript is unable to recognize that Err cannot possibly exist in this scenario. Is there a more elegant and straightforward approach to handle this issue?

To visualize the problem, I have set up a codesandbox: https://codesandbox.io/s/wild-architecture-nivu94?file=/src/index.ts:9-12

Answer №1

Check out the updated example I created to address your concerns here

I will be providing further explanation in this revised answer:

Problem

The error message 'Property 'value' does not exist on type 'Err<boolean, Error>'

Issues at Hand

In your code snippet, you have the following line present

ok(true) as Result<boolean, Error>

This implies that the method ok() is being assigned the type of Result<boolean, Error>. However, according to the types defined in the neverthrow documentation, the type of ok() should be Ok<T, E>

declare type Result<T, E> = Ok<T, E> | Err<T, E>;
declare const ok: <T, E = never>(value: T) => Ok<T, E>;
declare const err: <T = never, E = unknown>(err: E) => Err<T, E>;

The reason for the error lies in the fact that while the value of

ok(true) as Result<boolean, Error>
is Ok<boolean, Error>, its actual type can be either Ok<boolean, Error> or Err<boolean, Error>. Hence, if it turns out to be of type Err<boolean, Error>, it will not contain the property value. This warning from TypeScript highlights this issue

Property 'value' does not exist on type 'Err<boolean, Error>'

Solution

Instead of utilizing Result<boolean, Error>, consider using Ok<boolean, Error>

ok(true) as Ok<boolean, Error>

// or
ok<boolean, Error>(true)  // using ts generic

// recommended approach
ok(true)

Resolution

Specify the types of result explicitly

import { Err, ok, Ok, Result } from "neverthrow";

function resultIsError(
  result: Result<unknown, Error>
): result is Err<unknown, Error> {
  return result.isErr();
}

function doSomething() {
  const results: 
  [
   Ok<boolean, Error>,
   Ok<string, Error>,
   Ok<{ foo: string }, Error>
  ] = [
    ok(true),
    ok("foobar"),
    ok({ foo: "bar" })
  ];

  const resultErrors = results.filter(resultIsError);

  if (resultErrors.length > 0) {
    return resultErrors;
  }

  const [someBool, someString, someObj] = results;
  
  const someBoolValue = someBool.value;
  const someStringValue = someString.value;
  const someObjValue = someObj.value;

  console.log(someBoolValue, someStringValue, someObjValue);
}

doSomething()

Utilize dynamic types for result

import { Err, ok, Result } from "neverthrow";

function resultIsError(
  result: Result<unknown, Error>
): result is Err<unknown, Error> {
  return result.isErr();
}

function doSomething() {
  const results = [ok(true), ok("foobar"), ok({ foo: "bar" })];

  const resultErrors = results.filter(resultIsError);

  if (resultErrors.length > 0) {
    return resultErrors;
  }

  const [someBool, someString, someObj] = results;

  const someBoolValue = someBool.value;
  const someStringValue = someString.value;
  const someObjValue = someObj.value;

  console.log(someBoolValue, someStringValue, someObjValue);
}

doSomething();

Although someBoolValue may appear as a boolean when logged, its possible types could include

string | boolean | { foo: string; }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Encountered a problem when trying to access properties within a

Struggling to access properties of a nested object in TypeScript while using Angular, I encountered the following error: Object is possibly 'undefined'. Here is the code snippet: export interface Address{ city?: string; neighborhood?: string; } ...

In Angular 2, how does the "this" keyword from the subscribe method reference the class?

I am using a subscription for Observable, and when it finishes I need it to call a function from this class. The issue is that the "this" keyword refers to the subscription and not to the class scope. Here is the code snippet: export class GoogleMapCompo ...

Is there a way to show text as symbols in Primeng components?

Check out this helpful example that demonstrates what I am attempting to achieve. I have implemented p-menu from primeng. Is there a method to convert text into symbols like &trade to ™ and &#8482 to ®? ...

TypeORM ensures that sensitive information, such as passwords, is never returned from the database when retrieving a user

I developed a REST API using NestJs and TypeORM, focusing on my user entity: @Entity('User') export class User extends BaseEntity { @PrimaryGeneratedColumn() public id: number; @Column({ unique: true }) public username: string; publi ...

Create a custom data type that consists of a specific set of keys from an object

Is there a way to create a type in TypeScript that includes only a subset of keys from an object? Consider the example object below: const something = { cannary: "yellow", coyote: "brown", fox: "red", roses: "white", tulipan: "purple", palmera: ...

What is the best way to determine if a local storage key is not present?

By applying the if condition below, I can determine whether or not the local storage key exists: this.data = localStorage.getItem('education'); if(this.data) { console.log("Exists"); } To check for its non-existence using an if conditi ...

The TypeScript compiler throws an error when encountering nulls in conjunction with the isNull function

Whenever I set strictNullChecks: true in tsconfig.json and utilize the isNull function for null checks, the compiler throws the error TS2531: Object is possibly 'null'. Interestingly, isNull doesn't trigger any errors in VsCode, however, the ...

I am seeking guidance for developing my own messaging platform, similar to Discord, using Typescript

Allow me to simplify this for you. This piece of code outlines TypeScript interfaces and namespaces for a WebSocket API that is commonly used in a chat or messaging system. It seems to define the format of messages being exchanged between a client and ser ...

Does Angular 8 development mode implement tree-shaking?

I am curious to know if tree-shaking occurs during Angular 8 development mode. When running the following command: ng build I understand that tree-shaking happens when I use the command below: ng build --optimization=true|false ...

Is the 'case' in a switch statement not treated as a typeguard by Typescript?

Here is a simplified version of the code I am working with: type Todo = { id: string; text: string; }; type Action = | { type: 'DELETE'; payload: string } | { type: 'CREATE'; payload: Todo } function reducer(state: Todo[], ...

Changing a d3 event from JavaScript to Typescript in an Angular2 environment

I am a beginner in Typescript and Angular 2. My goal is to create an Angular2 component that incorporates a d3js tool click here. However, I am facing challenges when it comes to converting it to Typescript. For instance, I am unsure if this code rewrite ...

Guide on linking enum values with types in TypeScript

My enum type is structured as follows: export enum API_TYPE { INDEX = "index_api", CREATE = "create_api", SHOW = "show_api", UPDATE = "update_api", DELETE = "destroy_api" }; Presently, I have a f ...

Using TypeScript to automatically deduce the output type of a function by analyzing the recursive input type

I am currently working on developing an ORM for a graph database using TypeScript. Specifically, I am focusing on enhancing the "find" method to retrieve a list of a specific entity. The goal is to allow the function to accept a structure detailing the joi ...

Node C++ Addon Typescript declaration file

I have developed a Node C++ Addon that wraps a class similar to the one outlined in the official Node documentation. By using require(), I am able to access my addon and retrieve the constructor for my class in order to instantiate it. const { MyClass } = ...

The challenge of handling Set type in TypeScript errors

I'm currently facing two errors while trying to convert a function to TypeScript. The issue lies with the parameters, which are of type Set import type {Set} from 'typescript' function union<T>(setA: Set<T>, setB: Set<T>) ...

Error message 2339 - The property 'toggleExpand' is not recognized on the specified type 'AccHeaderContextProps | undefined'

When utilizing the context to share data, I am encountering a type error in TypeScript stating Property 'toggleExpand' does not exist on type 'AccHeaderContextProps | undefined'.ts(2339). However, all the props have been declared. inter ...

Error: Unable to use import statement outside of a module when deploying Firebase with TypeScript

Every time I try to run firebase deploy, an error pops up while parsing my function triggers. It specifically points to this line of code: import * as functions from 'firebase-functions'; at the beginning of my file. This is a new problem for me ...

Material UI - The array is unexpectedly resetting to contain 0 elements following an onChange event triggered by the TextField component

As I work on developing an application, one of the key features involves allowing users to select others from a list with whom they can create a group chatroom. Additionally, there is a TextField where they can assign a name to their newly created group. ...

Is it possible for a button to be assigned to a variable upon creation, but encounter an error when trying to

const button:Element = document.createElement("button");//This works fine const button:HTMLButtonElement = document.createElement("button");//This works too const button2:Element = document.getElementsByTagName("button");//Why does this give an error? con ...

Is there a way to update the text of a button when it is clicked?

Is there a way to dynamically change the text of a button when it is clicked and revert back to its original text when clicked again? I have attempted something along these lines, but I am unsure how to target the text since there isn't a property si ...