Nested interfaces can utilize sum types

An example showcasing the use of sum types:

interface Cash {
  amount: number,
  type: 'cash'
}

interface Card {
  amount: number,
  type: 'card',
  cardNumber: string
}

type Payment = Cash | Card

const displayPayment = (payment: Payment) => {
  if (payment.type === 'card') {
    console.log(`The payment of ${payment.amount} was made using ${payment.type} with card number ${payment.cardNumber}`)
  }
  else {
    console.log(`The payment of ${payment.amount} was made using ${payment.type}`)
  }
}

displayPayment({ type: 'cash', amount: 100 })
displayPayment({ type: 'card', amount: 100, cardNumber: '0123456789' })

Here's another similar example that is no longer functional

interface Cash {
  amount: number,
  // type now resides within a metadata property
  metadata: {
    type: 'cash'
  }
}

interface Card {
  amount: number,
  cardNumber: string
  // type now resides within a metadata property
  metadata: {
    type: 'card'
  }
}

type Payment = Cash | Card

const displayPayment = (payment: Payment) => {
  if (payment.metadata.type === 'card') {
    // Error: Property 'cardNumber' does not exist on type 'Payment'. Property 'cardNumber' does not exist on type 'Cash'.
    console.log(`Error: Unable to process payment. Amount: ${payment.amount}, Method: ${payment.metadata.type}, Card Number: ${payment.cardNumber}`)
  }
  else {
    console.log(`Unable to process payment. Amount: ${payment.amount}, Method: ${payment.metadata.type}`)
  }
}

displayPayment({ metadata: { type: 'cash' }, amount: 100 })
displayPayment({ metadata: { type: 'card' }, amount: 100, cardNumber: '0123456789' })

Why doesn't the sum type functionality work in this case?

It seems like TypeScript struggles to determine the specific type (cash or card) when the defining factor is not located at the outermost layer of the interface (in this scenario, within metadata).

Answer №1

Control flow analysis (the term used for TypeScript's ability to narrow types based on code) does not currently have support for nested discriminant properties in discriminated unions.

While a proposal for this feature has been made here, it seems that there hasn't been enough demand to push for its implementation just yet.

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

Using the HTTP Post method to retrieve a file object: a step-by-step guide

Is there a way to utilize a http POST request in order to retrieve a file object? Though the uploading of files to the server using the POST request seems successful and flawless, attempting to fetch the file results in an unusual response: console output ...

Challenge with module declaration in index.d.ts during upgrade from Angular 8 to 9 (excluding Material)

In my index.d.ts file, I have declared two modules like so: declare module 'googlemaps'; declare module 'detect-resize'; Previously, these declarations worked perfectly fine, allowing me to utilize these modules. The googlemaps module ...

Unable to resolve every parameter

I am facing an issue while trying to inject a service into my component, resulting in the following error: https://i.stack.imgur.com/zA3QB.png news.component.ts import { Component,OnInit } from '@angular/core'; import { NewsService } from &apo ...

Is there a way to determine if silent login is feasible with adaljs-angular4?

I'm currently utilizing the library [email protected] for an Angular 6 project. I am attempting to achieve the following: If a silent login (login without requiring user input) with Office365 is achievable, then perform a silent login (using ...

What is the process for changing a field in a document on firestore?

Is there a way to modify a specific field in a firestore document without retrieving the entire document beforehand? ...

Can a single file in NextJS 13 contain both client and server components?

I have a component in one of my page.tsx files in my NextJS 13 app that can be almost fully rendered on the server. The only client interactivity required is a button that calls useRouter.pop() when clicked. It seems like I have to create a new file with ...

Obtain values from a specific set, and then filter out values from an array that correspond to the index of that set into distinct arrays

The Problem I'm Facing Currently, I am dealing with a large data table that contains a mix of relevant and irrelevant data. My goal is to filter out the information I care about and display it in a more concise table. Using RegEx, I have managed to i ...

Is it possible for the ionic ionViewDidEnter to differentiate between pop and setRoot operations?

I am facing an issue with my ionic 3 page where I need to refresh the data on the page only if it is entered via a navCtrl.setRoot() and not when returned to from a navCtrl.pop(). I have been using ionViewDidEnter() to identify when the page is entered, bu ...

Having issues with @ts-ignore in Typescript on a let variable that is not reassigned?

JOURNEY TO THE PROBLEM My current task involves destructuring a response obtained from an Apollo useLazyQuery, with the intention to modify one variable. In a non-Typescript environment, achieving this would be straightforward with just two lines of code: ...

A Guide to Iterating Through Arrays of Objects Using TypeScript

Currently, I am engrossed in an Angular project where I am fetching an object containing an array of objects from an API. The object being passed to the API as a parameter through my service is called "reportData". Here is an example of the data retrieve ...

Utilizing the useSelect hook in Typescript to create custom types for WordPress Gutenberg, specifically targeting the core/editor

As I delve into development with WordPress and the Gutenberg editor, my goal is to incorporate TypeScript into the mix. However, I encounter a type error when trying to utilize the useSelect() hook in conjunction with an associated function from the core/e ...

Trigger event when ngModel changes

Currently, I am trying to perform a test on a select element... <select [ngModel]="selectedRouters" name="routerName" class="form-control" id="routersSelect" size="12" (ngModelChange)="selectRouters($event)" multiple> <option [value]="route ...

transform JSON structure into an array

Is it possible to convert an interface class and JSON file into a list or array in order to work on it? For example, extracting the Racename from each object in the JSON file and storing it in a list/array. Here is the interface structure: interface IRunn ...

Practical strategy for developing and launching a TypeScript/Node.js application

I'm currently developing a node.js app using Typescript, which requires compilation to JS before running. As someone with a background in java/jvm, I'm hesitant about the deployment process where code is pushed to git, built/compiled on the serve ...

What is the best way to reduce the size of TypeScript source code in an Electron application with the help of Electron Forge and Electron Packager

resolved: I was able to solve this issue using electron-builder, which utilizes webpack in the background to handle all problems efficiently. Initially, I faced this challenge while using electron-forge and electron-packager. Despite researching extensivel ...

Trigger on the cancellation or completion of a nested observable

I'm seeking a way to detect if an inner observable was not successfully completed (due to everyone unsubscribing) and then emit a value in that scenario. Something akin to defaultIfEmpty, but the current solution isn't effective. A trigger exis ...

Tips for effectively managing loading and partial states during the execution of a GraphQL query with ApolloClient

I am currently developing a backend application that collects data from GraphQL endpoints using ApolloClient: const client = new ApolloClient({ uri: uri, link: new HttpLink({ uri: uri, fetch }), cache: new InMemoryCache({ addTypename: f ...

Struggling with getting Typescript async/await to function properly

I'm experiencing an issue with async/await in TypeScript targeting es2017. Here is the code snippet that's causing trouble: My route.ts : method: 'POST', config: { auth: { strategy: &apo ...

The object is classified as 'undetermined' (2571) upon implementation of map() function

Despite conducting a thorough search about this error online, I still haven't been able to find a solution. Let's jump into an example with data that looks like this: const earthData = { distanceFromSun: 149280000, continents: { asia: {a ...

Creating Typescript types based on the values of other props: A guide

Can the TypeScript prop type be dynamically changed based on the runtime value of another prop? For instance type MyComponent = { propA: boolean | string propB: typeof propA boolean ? number : string } Is it feasible to determine the prop type of p ...