Using optional function arguments with destructured arguments in TypeScript can result in throwing an error

interface Type1 {
  attr1: string;
  attr2: string;
}
interface Type2 {
  attr1: string;
  attr2: string;
  attr3: string; // additional attribute
}

function fn(config: Type1 | Type2): void {
  // The error code is displayed above. I am aware that one solution is to add an optional attribute to 'attr3'. However, in my opinion, this solution is not ideal. In reality, there are only two possible situations - either 'Type1' or 'Type2'. Therefore, making the attribute optional does not improve readability. Is there a more advanced way to resolve this issue?</p>

Answer №1

You can easily verify the existence of attr3 in the object.

function fn(config: Type1 | Type2): void {
  if ('attr3' in config) {
    const {
      attr1,
      attr2,
      attr3
    } = config;
  } else {
    const {
      attr1,
      attr2
    } = config;
  }
}

Alternatively, you could utilize a custom type guard

function IsType2(config: Type1 | Type2): config is Type2 {
  return (config as Type2).attr3 !== undefined;
}

function fn(config: Type1 | Type2): void {
  if (IsType2(config)) {
    const {
      attr1,
      attr2,
      attr3
    } = config;
  } else {
    const {
      attr1,
      attr2
    } = config;
  }
}

If we really only wanted to destructure once, we could create a join type, although it would involve coercing the type instead of inferring it.

function fn(config: Type1 | Type2): void {
  const {
    attr1,
    attr2,
    attr3
  } = config as Type1 & Type2;
}

To my knowledge, there isn't a direct way to destructure a union type.

Answer №2

To achieve the same outcome when missing the attr3 attribute as you would with JavaScript code, simply convert the config type to an intersection of types:

interface Type1 {
  attr1: string;
  attr2: string;
}
interface Type2 {
  attr1: string;
  attr2: string;
  attr3: string; // extra attribute
}

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

function fn(config: Type1 | Type2): void {
  // Property 'attr3' does not exist on type 'Type1 | Type2'.ts(2339)
  const { attr1, attr2, attr3 } = config as UnionToIntersection<Parameters<typeof fn>[0]>;
  console.log(attr1);
  console.log(attr2);
  console.log(attr3);
}

fn({ attr1: '', attr2: '' })

Alternatively, you can use

... = config as Type1 & Type2;
if some rigidity is acceptable.

Visit TS playground for more details.

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

The Same Origin Policy has prevented access to the remote resource located at http://localhost:8082/api/countries due to a Cross-Origin Request Block

Solution The XMLHttpRequest access to 'http://localhost:8082/api/countries' from the origin 'http://localhost:4200' has been blocked by the CORS policy. The response to the preflight request is failing the access control check because t ...

Setting the default selected row to the first row in ag-Grid (Angular)

Is there a way to automatically select the first row in ag-grid when using it with Angular? If you're curious, here's some code that may help: https://stackblitz.com/edit/ag-grid-angular-hello-world-xabqct?file=src/app/app.component.ts I'm ...

Leverage tsconfig.json for TypeScript compilation in Vim using the Syntastic plugin

How can I configure the syntastic plugin in vim to provide live error checking for TypeScript files using tsc? Currently, even though I have tsc set up in vim, it doesn't seem to be using the closest parent's tsconfig.json file for configuration. ...

There is no matching signature for Type when using withStyles

Struggling to convert my React App to typescript, I keep encountering the error below and cannot decipher its meaning. The app functions perfectly in plain JS. My package version is material-ui@next TS2345: Argument of type 'typeof ApplicationMenu&a ...

Chart.js Axis Labels Orientation Guidelines

I am currently utilizing chart.js within an Angular 8 environment using Primeng. I am looking to customize the options for my chart as follows: For the y-axis ticks, set textDirection to 'ltr' For the x-axis ticks, set textDirection to 'rtl ...

Is it possible that a declaration file for module 'material-ui/styles/MuiThemeProvider' is missing?

I have been trying to implement the react material-ui theme after installing it via npm. However, I am encountering errors when adding 'import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";' in boot-client.tsx: TS7016: Could not ...

A guide on implementing isomorphic types in TypeScript

Consider the following scenario with two files, file.ts: let a: Message = "foo" let b: Message = "bar" let c: Message = "baz" Now let's introduce a second file, file2.ts type Message = string function fun(abc: Message): void { } When using functi ...

How can one properly conduct a health check on a Twilio connection using TypeScript?

How can I create an endpoint in TypeScript to verify if the Twilio connection is properly established? What would be the proper method to perform this check? Below is a snippet of my current code: private twilioClient: Twilio; ... async checkTwilio() { ...

Typescript is failing to perform type checking

I'm encountering an issue while trying to utilize TypeScript type checking with the following code snippet: abstract class Mammal { abstract breed(other: Mammal); } class Dog extends Mammal { breed(other: Dog) {} } class Cat extends Mammal { ...

Utilizing a JSDoc comment from an external interface attribute

Currently, I am in the process of developing a React application. It is common to want the props of a child component to be directly connected to the state of a parent component. To achieve this, I have detailed the following instructions in the interface ...

What Mac OSX command can you use in Typescript to input the quote character for multiline text?

Just starting out with Angular 2 and working through the official tutorial at https://angular.io/docs/ts/latest/tutorial/toh-pt1.html. I've realized that to use multi-line template strings (string interpolation), I have to use the ` mark. Any tips fo ...

What strategy does Node recommend for organizing code into multiple files?

In the midst of my current project, which involves NodeJS and Typescript, I am developing an HTML5 client that communicates with a NodeJS server via web-sockets. With a background in C#, I prefer to organize my code into separate files for different functi ...

The webpack development server refreshes the page just one time

I've been searching through various issues on Stack Overflow but haven't had any luck. It seems like most solutions are for an older version of webpack-dev-server. Despite trying numerous things, my app just won't reload or rebuild more tha ...

I encounter an error in my JavaScript function indicating that it is not defined

let element = document.querySelector("#value"); let buttons = document.querySelectorAll(".btn"); buttons.forEach(function (button) { button.addEventListener("click", function(event){ console.log(event.currentTarge ...

Angular Typescript Filter failing to connect with service injection

I am having trouble accessing the Constant app within a filter in Angular TypeScript. How can I successfully access a service inside a filter? Module App.Filter { import Shared = Core.Shared; export class MilestoneStatusFilter123 { static $inject = ...

Utilizing Generics in TypeScript to Expand Abstract Classes

How can I define the property eventList in the class ImplTestClass to be an array with all possible values of AllowedEvents, while extending the class TextClass that accepts T? I'm stuck on this one. If anyone can provide guidance on how to achieve t ...

Error encountered during conversion from JavaScript to TypeScript

I am currently in the process of converting JavaScript to TypeScript and I've encountered the following error: Type '(props: PropsWithChildren) => (any[] | ((e: any) => void))[]' is not assignable to type 'FC'. Type '(a ...

Implementing context menus on the Material-UI DataGrid is a straightforward process that can enhance the user experience

I am looking to enhance my context menus to be more like what is demonstrated here: Currently, I have only been able to achieve something similar to this example: https://codesandbox.io/s/xenodochial-snow-pz1fr?file=/src/DataGridTest.tsx The contextmenu ...

Troubleshooting Issue: Angular 6 - Authentication token interceptor not including headers

After experimenting with various approaches using Angular 6 for the past couple of days, I recently tried implementing a solution mentioned in this post: . Despite my efforts, the header in the requests still remains unset. import {Inject, Injectable} fro ...

Struggling with testing the checkbox when it changes inside the CardHeader Avatar={} component

I've recently implemented a feature similar to the example showcased on MaterialUI's TransferList. However, I'm encountering difficulties accessing a checkbox within the avatar={}. The project utilizes Jest and Enzyme for testing purposes. T ...