TypeScript throwing error: Attempting to call a potentially 'undefined' object goes against coding principles

In the snippet below, an error is encountered:

example/not-following.ts:15:1 - error TS2722: Cannot invoke an object which is possibly 'undefined'.

15 run(true).maybe();
   ~~~~~~~~~~~~~~~

Snippet:

interface Example {
    maybe?: () => void;
}

function execute(condition: boolean): Example {
    const object: Example = {};
    if (condition) {
        object.maybe = (): void => {
            console.log('maybe');
        };
    }
    return object;
}

execute(true).maybe();

Despite being deterministic code, TypeScript fails to track its flow. Why does this happen?

Answer №1

The solution was a technique known as generics. You can learn more about generics at this link: https://www.typescriptlang.org/docs/handbook/generics.html

interface Something {
    maybe?: () => void;
}

function execute<T extends boolean>(isTrue: T):
    T extends true
        ? Something & Required<Pick<Something, "maybe">>
        : Something {
    const object: Something = {};
    if (isTrue) {
        object.maybe = () => {
            console.log('maybe');
        };
    }

    // @ts-ignore
    return object;
}

execute(true).maybe();

// @ts-expect-error
execute(false).maybe();

Answer №2

An error occurs when trying to call an object that may be 'undefined'.

This issue arises because the variable maybe is optional in TypeScript, making it unclear whether it has been defined or not.

To resolve this error, you need to first verify if the property exists:

   interface Something {
        maybe?: () => void;
    }

    function execute(condition: boolean): Something {
        const obj: Something = { };
        if (condition) {
            obj.maybe = (): void => {
                console.log('maybe');
            };
        }
        return obj;
    }

    const someObject: Something = execute(true);
    if (someObject.maybe) {
        someObject.maybe();
    }

Answer №3

It seems that TypeScript is intelligent to some extent, but it may not be sophisticated enough to fully validate these types of runtime execution paths.

If you're looking for an alternative approach, utilizing closures could be another option, although its suitability for your specific case is uncertain.

interface Something {
    maybe: () => void;
}

function run(isTrue: boolean): Something {
  const object: Something = {
    maybe: () => {
      if (isTrue) {
        console.log('maybe');
      }
    }
  };
  return object;
}

run(true).maybe();

Note:

In response to the downvote I received (which I do not intend to contest), I would like to mention that to prevent errors in TypeScript, you should either:

  • make the maybe function mandatory, or
  • verify if the function is defined before each call.

I personally am not a fan of optional fields and do not wish to repeatedly enclose every call in an if statement. Thus, I still favor my current solution.

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

Changing JSON names to display on a webpage

I am looking to modify the name displayed in a json file for presentation on a page using ion-select. mycodehtml ion-select [(ngModel)]="refine" (ionChange)="optionsFn(item, i);" > <ion-option [value]="item" *ngFor="let item of totalfilter ...

Is there a way to implement retry functionality with a delay in RxJs without resorting to the outdated retryWhen method?

I'd like to implement a retry mechanism for an observable chain with a delay of 2 seconds. While researching, I found some solutions using retryWhen. However, it appears that retryWhen is deprecated and I prefer not to use it. The retry with delay s ...

Invoke a component's function in a different component

Upon clicking the button, I am trying to execute the 'exportHistoryToCSV' method within this component, which in turn calls a method from another component. However, I am encountering an error. @UntilDestroy() @Component({ selector: 'd ...

Securely import TypeScript modules from file paths that are dynamically determined during execution

Imagine you have a structure of TypeScript code and assets stored at a specific URL, like between a CDN and a debug location. You want to import the main module and ensure the rest of the structure is imported correctly only when needed, without repeating ...

Exploring ways to ensure robust typing for the body of NextApiRequest within a Next.js environment

Are you trying to figure out how to correctly define the body type of an API POST route in Next.js for better type safety? In NextApiRequest, the body is currently defined as "any" and NextApiRequest itself is not generic. I have tried forcefully assigni ...

Set up TypeScript to automatically deduce the type of generics based on a specific function name within the

My goal is to minimize redundancy in common code patterns within a project by setting up TypeScript to perform a sort of enchantment - deducing a generic type based on the existence of a function with a specific name. Picture me utilizing a framework that ...

Error: Query is not valid

I'm encountering an error when trying to run this query, and I'm not sure why it's returning a bad request. My goal is to fetch a specific user from the database using the accountId. Can someone assist me with this issue? Below is the funct ...

How can the `!` operator be utilized in MikroORM Typescript entities?

How can I declare a key in a JS object with an ! before the colon? MikroORM syntax for class @Entity() export class Post { // Using @PrimaryKey() decorator to designate primary key @PrimaryKey() id!: number; @Property({ type: "date", de ...

Having trouble resolving TypeScript TS2322 error with Context API + useState hook in your React App?

Currently, I am working on a React Typescript project that utilizes the Context API to pass down a useState hook. export const AppContext = React.createContext(null); function App() { const [value, setValue] = React.useState(3); return ( <Ap ...

Tips on distinguishing the original Ctrl+C and Ctrl+V commands from the Javascript-added document level listeners

My Clipboard service includes a copy() and paste() method that is triggered by Ctrl+C and Ctrl+V commands. These methods are document-level keyboard listeners added to a component using HostListeners. However, I am facing an issue where the paste() method ...

Issue encountered with the props type upon import, Ts(2322)

Seeking assistance with a TypeScript, StyledComponent, and React project using Create React App. Encountering an error during build time that cannot be ignored. // browser and terminal error TypeScript error in ./src/index.tsx(4,1): Type '{ alt: ...

What is the method for determining the constant array type based on its contents?

Suppose an array is declared as follows: const array = ['a', 'b', 'c']; The type of this array would be string[]. Is there a way to automatically determine the type based on the content, so that there is no need to specify th ...

The presence of this container with Mongoose Schema Typescript eclipses an external 'this' value

Here's my schema validator code for MongoDB: UserSchema.path('email').validate(async function (email: string) { const count = await User.count({ email, _id: { $ne: this._id } }) return !count }, 'Email already exists') However ...

Encountering a Problem with Angular 2 RC HTTP_PROVIDERS

I recently upgraded my Angular 2 application to the RC version. Everything was working smoothly until I included HTTP_PROVIDER and created a service.ts file. However, now I am encountering an error: (index):14 Error: SyntaxError: Unexpected token <( ...

Utilize TypeScript and HTTPS to send a message through WhatsApp

Trying to utilize TypeScript for sending a message through the WhatsApp cloud API has presented an issue – specifically, the error message "Parameter implicitly has an 'any' type." While I have successfully executed this in Node.js, adapting it ...

Securing React: Best Practices for State Management

Consider the following scenario: const UserProfile: React.FC<RouteComponentProps> = (props) => { const { isAdmin} = useContext(GlobalContext); if (isAdmin()) { return <CriticalFeature/>; } else { return <NonCritic ...

Getting the Most Out of .find() in Angular 4

Despite reading various similar questions, I'm still struggling to make the .find() function work in my application. I have a service with the following API: export class VehicleService { private defUrl = 'API'; constructor(private ht ...

A Guide to Retrieving Parameters and Request Body using Express and Typescript

When I use the PUT method, I encounter this issue: const createFaceList = (req: Request<{faceListId : string}>, res: Response, next: NextFunction) => { console.log(req.body.name); console.log("faceListID = " + req.params.faceListId); a ...

Error: Cannot locate 'import-resolver-typescript/lib' in jsconfig.json file

Issue: An error occurred stating that the file '/Users/nish7/Documents/Code/WebDev/HOS/frontend/node_modules/eslint-import-resolver-typescript/lib' could not be found. This error is present in the program because of the specified root file for c ...

Mapping specific properties in an object using Typescript

I'm diving into TypeScript and aiming to create a straightforward generic function. The function will take an object (potentially partial), a list of keys, and map the specified keys (if they exist) to a different value while keeping the rest of the v ...