The or operator in Typescript does not function properly when used as a generic argument

My current configuration is as follows:

const foo = async <T>(a): Promise<T> => {
  return await a // call server here
}

type A = { bar: 'bar' } | { baz: 'baz' }
foo<A>({ bar: 'bar' })
  .then(response => {
    // error: property bar doesn't exist on type { baz: 'baz' }
    if (response.bar) console.log('bar') 

    // error: property baz doesn't exist on type { bar: 'bar' }
    if (response.baz) console.log('baz')
  })

Do you think this issue lies with TypeScript or my particular setup?

Answer №1

Regrettably, Typescript does not narrow types down when performing a simple truthy test on a property like `if (foo.bar) {}`. This is likely due to the dynamic nature of properties in JavaScript, such as property getters, proxies, and returning values like 0, false, or null. In other words, the expression `if (obj.prop)` carries more implications than just checking for the existence of a property.

Fortunately, JavaScript features a special keyword called `in`, which explicitly verifies the existence of a property on an object.

Therefore, by using `if ('bar' in foo)`, Typescript can confidently refine the type for `bar`.

In your specific scenario:

if ('bar' in response) console.log('bar')

will no longer trigger an error, and additionally, `response` will now include a property named bar.

if (`bar` in response) console.log(response.bar)

In a proficient editor, after typing `response` within that `console.log(`, the editor will suggest `bar` as an option.

Answer №2

To handle this scenario differently, you can utilize type assertion :

const function = async <T>(param): Promise<T> => {
    return await param // make server call here
}

type Dog = { breed: 'bulldog' }
type Cat = { breed: 'siamese' }
type Pet = Dog | Cat

function<Pet>({ breed: 'bulldog' })
    .then(result => {
        if ((result as Dog).breed) console.log('bulldog')
        if ((result as Cat).breed) console.log('siamese')
    })

Nevertheless, I advise against using this approach. For a more intricate situation like yours, consider the following alternative:

type Dog = { kind: 'dog', breed: 'bulldog' }
type Cat = { kind: 'cat', breed: 'siamese' }
type Pet = Dog | Cat

function<Pet>({ breed: 'bulldog' })
    .then(result => {
        switch (result.kind) {
            case 'dog':
                console.log(result.breed)
                break;
            case 'cat':
                console.log(result.breed)
                break;
            default:
                console.log('unknown kind')
        }
    })

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

Accessing props in setup function in Vue 3

I am encountering issues when trying to access the props value (an array) in my composition API setup. The component I have is called DropDown, and I am passing it an array of objects. Here's what I need to achieve: export default { emits: ['up ...

What is causing the failure of the state to be inherited by the child component in this scenario (TypeScript/React/SPFX)?

For this scenario, I have a Parent class component called Dibf and a Child class component named Header. While I can successfully pass props from the Parent to the child, I am encountering difficulties when trying to pass state down by implementing the fo ...

The functionality to generate personalized worldwide timezone pipe is not functioning

I'm completely new to Angular and I've been working on creating a custom pipe for adjusting timezones. The idea is to allow users to select their preferred timezone and have the offset applied accordingly. To start, I created a file called timez ...

Async function causing Next JS router to not update page

I'm diving into the world of promises and JavaScript, but I've encountered an issue while working on a registration page following a tutorial on YouTube. Currently, I am using next.js with React and TypeScript to redirect users to the home page i ...

Unable to send a function as props to a child component in TypeScript

Within my application, I have a parent component that holds a state and a setState function. This state and function are then passed down to a child component in order to retrieve values (such as input field data). When the function is triggered in the chi ...

"iOS users have reported that notifications from Firebase have mysteriously ceased to

Yesterday evening, I was experimenting with Push Notifications from Firebase on my iOS application and everything was functioning correctly. I successfully sent a notification from a Cloud Function to a specific FCM token. However, this morning, notificat ...

Using ngFor to display a default image

My latest project involved creating a table that displays various products along with their corresponding images. Everything was working smoothly until I encountered an issue. In instances where a product is created without an associated image, I decided ...

Guide to utilizing @types/node in a Node.js application

Currently, I am using VSCode on Ubuntu 16.04 for my project. The node project was set up with the following commands: npm init tsc --init Within this project, a new file named index.ts has been created. The intention is to utilize fs and readline to read ...

What is the recommended TypeScript type for the NextJS _app.tsx Component and pageProps?

Take a look at the default _app.tsx code snippet from NextJS: function MyApp({ Component, pageProps }) { return ( <Component {...pageProps} /> ) } The issue arises when transitioning to TypeScript, as ES6Lint generates a warning indicating t ...

AngularTS regex that enforces the use of a decimal point in numbers

I am working on a requirement where the input should only accept decimal characters, negative or positive. I need to use regex to make the decimal point mandatory, however it is currently allowing negative whole numbers which is not the desired behavior. I ...

Redirect user to the "Confirm Logout" page in Keycloak after refreshing the page before logging out

While working on a project with keycloak, I've encountered an issue that I can't seem to figure out. After logging in and navigating to my project's page, everything operates smoothly. However, if I happen to refresh the page before logging ...

What is the best way to specify the type of a property that has already been assigned

I am currently utilizing a third-party library that includes a type defined as the following: export interface ThirdPartyNodeType { id: string; name: string; data: any; } Upon further exploration, I have identified the content that I intend to include ...

Having trouble with the npm Fluid Player installation

I am attempting to integrate Fluid Player into my Angular application Using - npm i fluid-player However, I'm encountering this error ...

Encountered an error in React where the declaration file for a module could not be located

New to Typescript and trying to incorporate a splitter into my project. I'm utilizing SplitPane from "react-split-pane/lib/SplitPane" and Pane from "react-split-pane/lib/Pane" in my Typescript project, but encountering an error: Could not find a de ...

NestJs Function yielding inconsistent results based on its calling location

There is a puzzling issue that I am unable to solve. I have stored priceHistories in memory within an array. Strangely, when I invoke a get method, the returned value varies depending on where the method is called from. This is the original property and m ...

Guide to simulating a function using .then within a hook

I am using a function that is called from a hook within my component. Here is my component: ... const handleCompleteContent = (contentId: string) => { postCompleteContent(contentId, playerId).then(data => { if (data === 201) { ... The caller ...

An error occured in angular2: Cannot access the 'title' property of undefined

Here is the code snippet for my custom component: export class MoviedetailComponent implements OnInit { movie:any constructor( private getmovie: GetmovieService, private router: Router, private rout: ActivatedRoute ) { } ngOnInit() { this.r ...

The feature of getDisplayMedia is not included in TypeScript 3.8

I am currently developing a .Net Core/Angular 8 application in Visual Studio. Within the Angular (ClientApp) section of my project, I have TypeScript 3.5.3 located in node_modules, which includes the following definition in lib.dom.d.ts: interface Navigat ...

Global Enum in Typescript that is Optimized for Inlining during Compilation

I'm facing a challenge with an enum that is widely used in my project. Having to import it into every file is becoming cumbersome. Is there a way to define the enum in the .d.ts file so that it automatically gets included when compiled to Javascript? ...

Unable to execute function on Child Element

I am encountering an issue when trying to call a function on a child component. Whenever I try to invoke it by clicking, I always receive an error indicating that the function is undefined. Here is the code in my Parent component: import {MatTableCompone ...