The type 'ElementTypes' cannot be assigned to type 'ElementTypes.word'

After recently learning TypeScript, I encountered an error that made me think I need to write a narrower type for it or something along those lines.

Here is the code snippet in question:

enum ElementTypes {
  h1 = 'H1',
  word = "WORD"
}

type DefaultElementType<T> = {
  children: string[];
  id: number | string;
  type: T;
};

type H1Element = DefaultElementType<ElementTypes.h1>;
type WordElement = DefaultElementType<ElementTypes.word> & {
  value: string;
}

type CustomElement =
  | H1Element
  | WordElement

const insertNode = (element: CustomElement) => {
  // do sth here, not important
}

const addInsertNode = ({type, id}: {type: ElementTypes, id: number | string}) => {
  insertNode({type, id, children:[], ...(type === ElementTypes.word ? {value: 'some value'}: {})})
}

And here is the error message it generated:

Argument of type '{ value?: string | undefined; type: ElementTypes; id: string | number; children: never[]; }' is not assignable to parameter of type 'CustomElement'.
  Type '{ value?: string | undefined; type: ElementTypes; id: string | number; children: never[]; }' is not assignable to type 'WordElement'.
    Type '{ value?: string | undefined; type: ElementTypes; id: string | number; children: never[]; }' is not assignable to type 'DefaultElementType<ElementTypes.word>'.
      Types of property 'type' are incompatible.
        Type 'ElementTypes' is not assignable to type 'ElementTypes.word'.

I attempted to use Discriminated Unions to address this issue, but unfortunately, it did not resolve it.

Here is a playground showcasing the error.

Any help on this matter would be greatly appreciated.

Answer №1

When analyzing TypeScript code, each expression is only analyzed once. If an expression contains multiple occurrences of the same value from a union type, the compiler treats each value independently rather than considering all combinations. This can result in issues with correlated unions as highlighted in microsoft/TypeScript#30581.

A specific scenario inside the function addInsertNode() involves handling the union type ElementTypes. Due to this union type, the correlation between certain values is lost, leading to potential errors when trying to assign a value to CustomElement.


To address this issue, there are different approaches you can take. One simple solution is to use type assertions to maintain your existing code structure:

const addInsertNode = ({ type, id }: { type: ElementTypes, id: number | string }) => {
  insertNode({
    type, id, children: [],
    ...(type === ElementTypes.word ? { value: 'some value' } : {})
  } as CustomElement)
}

While this approach bypasses some type safety checks by the compiler, it provides a quick fix.


For those seeking rigorous type validation, refactoring might be necessary. One strategy involves isolating each union member in separate expressions using narrowing:

const addInsertNode = ({ type, id }: { type: ElementTypes, id: number | string }) => {
  insertNode(
    (type === ElementTypes.word) ?
      { type, id, children: [], value: 'some value' } :
      { type, id, children: [] }
  )
}

Although this method introduces redundancy, it ensures that each expression has a clearly defined type for the compiler to analyze.


An alternative approach involves leveraging generics to represent operations instead of unions. This technique is elaborated further in microsoft/TypeScript#47109. By defining key-value mapping types and utilizing generic indexes, you can enhance type inference within your code.

Don't forget to check out the accompanying Playground link for hands-on experience with the suggested changes.

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

Symfony using Vite with Vue 3 encounters an error: Uncaught ReferenceError - exports is undefined

Currently, I am in the process of developing a Symfony 3 Application with Vite and Vue3 integrated with TypeScript. To replace Symfony's Webpack Encore, I opted for the Vite Buildtool using this convenient plugin: https://github.com/lhapaipai/vite-bu ...

Is there a way to combine multiple array objects by comparing just one distinct element?

Is there a way to combine these two arrays into one? array1 = [ { image: 'image1', title: 'title1' }, { image: 'image2', title: 'title2' }, { image: 'image3', title: 'title3' }, ]; array2 = ...

Error in Next.js when trying to use Firebase Cloud Messaging: ReferenceError - navigator is not defined in the Component.WindowMessagingFactory instanceFactory

Currently, I am in the process of setting up push notifications with Firebase in my next.js application. I have been following a guide from the documentation which you can find here: https://firebase.google.com/docs/cloud-messaging/js/receive?hl=es-419 Ho ...

What is the best way to specify data types for all attributes within an interface?

In building an interface for delivering strings in an i18n application: interface ILocaleStringsProvider { 'foo': string 'bar': string 'baz': string 'blablabla': string // numerous other string properties ...

The Vercel/NextJS deployment does not delay the completion of the serverless function until the email is sent via Azure

Upon a user's registration, I am attempting to send a registration/account activation email. While the email sends successfully via Azure's email services when running on localhost, deployments on Vercel do not trigger the email (although the use ...

Is it possible to execute "green arrow" unit tests directly with Mocha in IntelliJ IDEA, even when Karma and Mocha are both installed?

My unit tests are set up using Karma and Mocha. The reason I use Karma is because some of the functionality being tested requires a web browser, even if it's just a fake headless one. However, most of my code can be run in either a browser or Node.js. ...

Ways to implement logging in an NPM package without the need for a specific logging library

Currently, I am in the process of developing a company npm package using TypeScript and transferring existing code to it. Within the existing code, there are instances of console.log, console.warn, and console.error statements, as shown below: try { c ...

"Using an indexer in TypeScript allows for direct access to object properties by simply specifying the key within

I have a requirement to access an object property using a string as the key interface MyObject { prop1: string; prop2: string; prop3: string; prop4: string; prop5: string; } let initialValues: MyObject; //I initialize some properties initialVa ...

Generating a fresh array based on the size of its existing elements is the key feature of the ForEach method

When running this forEach loop in the console, it extracts the property "monto_gasto" from an array of objects in the firebase database. Here's how it looks: something.subscribe(res => { this.ingresos = res; ...

Is it possible to replicate a type in TypeScript using echo?

Is there any equivalent in TypeScript to the following code snippet? type TypeA = { x: number }; printType(TypeA); I have found a method that consistently enables TypeScript to provide a type description. const y = { x: 1, z: 'hello', }; ...

Tips for ensuring a method is not invoked more than once with identical arguments

I'm grappling with a challenge in JavaScript (or typescript) - ensuring that developers cannot call a method multiple times with the same argument. For instance: const foo = (name: string) => {} foo("ABC") // ok foo ("123") ...

Sidenav selector unable to display Angular component

I'm facing a dilemma. I have the following code in my app.component.html file: <mat-sidenav-container class="sidenav-container"> <app-sidenav></app-sidenav> <mat-sidenav-content> <app-header></app-header> ...

How can debugging in Chrome be achieved using Typescript?

How is it possible to debug TypeScript in Google Chrome when the browser only understands JavaScript? I find myself debugging my TypeScript files within my Angular project, which was created using Angular CLI, through the Chrome developer tools. However, ...

Could you please explain the specific distinctions between pipe and map within Angular 7?

After extensive research, I'm still struggling to understand the distinction between pipe and map in Angular 7. Should we always include a pipe in Service.ts file in Angular 7? Appreciate any clarification on this matter. ...

The html-duration-picker is not being displayed in the proper format

I've been working on integrating an external library that allows for inputting document length. Specifically, I'm using the html-duration-picker library, but it seems like the input functionality is not quite right for durations. Could it be th ...

The elixir-typescript compilation process encountered an error and was unable to complete

I am currently working on integrating Angular2 with Laravel 5.2 and facing an issue with configuring gulp to compile typescript files. Below is a snippet from my package.json file: { "private": true, "scripts": { "prod": "gulp --production", ...

The breakpoint was overlooked due to the absence of generated code for TypeScript on a Windows operating system

Currently, I am in the process of debugging a TypeScript project. The structure of the project folder and tsconfig.json file is illustrated below: Furthermore, my launch.json file is displayed below: While attempting to debug, I have noticed that .map fi ...

Entering key-value pairs into a dictionary to show correlation

I've been struggling to find a solution for what seems like a simple issue. The problem lies in typing a dictionary with values of different types so that TypeScript can infer the type based on the key. Here is the scenario: type Id = string; inter ...

A guide on incorporating Typescript into Material UI v5 themes

A similar question has been asked previously, however... I am looking to enhance my color options by adding variants such as success, warning, and more choices within the background category (palette.background). Specifically interested in a lite option t ...

What are the steps to resolve the UglifyJs error stating 'Unexpected token operator'?

When running the following command in my Angular app: > ng build --prod --aot --env=staging I encounter this error message: ERROR in vendor.0625f773941bc83e6748.bundle.js from UglifyJs Unexpected token operator «*», expected punc «(» [vendor.0625 ...