When attempting to pass a numeric value to the @Input x property with two-way binding in Angular's strict template mode, an error is triggered

I am looking to enhance my application by implementing Angular's template Strict mode through the configuration of

"strictTemplates": true
within tsconfig.json. However, encountering an unexpected error after running ng serve with this updated configuration.

One issue I have is with a shared component that is extensively used throughout the application.

@Component({
  selector: 'my-shared-component',
  ...
})
export class SharedComponent {
  @Input() selectedId: string | number;
  @Output() selectedIdChange = new EventEmitter<string | number>()
  @Input() disabled: boolean;
  ...
}

Specifying

<my-shared-component disabled="true">
triggers an error, which is understandable.

The real challenge arises when attempting to pass a number into the

@Input() selectedId: string | number;
, such as:

export class OtherComponent {
  myNumberId: number;
  ...
}

<my-shared-component [(selectedId)]="myNumberId">

An error is then thrown:

TS2322: Type 'string | number' is not assignable to type 'number'

Refactoring the data type of myNumberId to string | number is not a feasible solution since it is also utilized in another component where @Input id: number is required (and must remain a number). Moreover, myNumberId originates from the server and its type is known to be a number, making the replacement with string | number seem inadequate.

Disabling the strictAttributeTypes configuration is not desirable either, as it would eliminate errors triggered by

<my-shared-component disabled="true">
.

I am exploring the possibility of using a typescript utility type (or similar tool) to restrict

@Input() selectedId: string | number
to only accept strings or numbers, without mandating the variables from the parent components to be string | number. Alternatively, I am open to other suggestions for resolving this issue.

Answer №1

It's completely understandable that an error is thrown - the EventEmitter output will emit either a string or a number, which conflicts with assigning a value to your strictly typed number.

To resolve this issue, one approach you can take is separating the setter and getter in your OtherComponent:

_myNumberId: number;

get myNumberId(): number {
    return this._myNumberId;
}

get myNumberId(value: string | number): number {
    if (typeof value === 'string') {
        // You may want to use parseFloat() or another method instead of parseInt()
        this._myNumberId = parseInt(value, 10);
    } else {
        this._myNumberId = value;
    }
}

This method allows for preserving the number type for check types when fetching values, while still enabling assignment from both string and number, eliminating the compilation error. However, this solution is not ideal as it appears to be a code smell and compromises type checking on the setter.

An alternative perspective would be to reconsider the concept of an "Id" within your application and utilize a generic component using a defined type, such as:

export type MyId = string | number; 

Subsequently, your component could leverage generics by extending this type, ensuring strict type validation:

@Component({
  selector: 'my-shared-component',
  ...
})
export class SharedComponent<T extends MyId> {
  @Input() selectedId: T;
  @Output() selectedIdChange = new EventEmitter<T>()
  @Input() disabled: boolean;
  ...
}

This setup enables the assignment of string or number values in two-way binding, enforcing a single type throughout. If further logic adjustments are necessary for the Id, modifications can be made within the type definition, allowing for easy identification of any type violations.

Alternatively, you have the option to forego defining the type explicitly and structure it like this:

export class SharedComponent<T extends string | number> {

However, considering potential enforcement requirements across different contexts, establishing a custom value type appears to be a more robust solution.

For a demonstration of the generic approach correctly handling both string and number assignments in two-way binding, with an intentional failure when trying to assign a different type (Date), feel free to explore this stackblitz example.

Answer №2

Consider substituting string | number for string & number.

Additionally, experiment with sending it in the following format:

<my-component [(id)]="+numberId">

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

Challenges with image cropping in Angular causing performance problems

Utilizing this specific component for image cropping within an ionic/angular8 project has been causing severe performance issues, leading to unresponsiveness on mobile devices. Interestingly, the desktop version does not encounter any problems and the crop ...

What is the best way to include a new attribute in a TypeScript internal object?

I am trying to include a property declaration in the window.history object, but I received a TypeScript error message This is my code: const historyInstance = createHashHistory(); // npm hoistory module window.history.historyInstance = historyInstance; / ...

Steps to prevent uib-timepicker from automatically adjusting time based on the Browser Timezone

Currently in my database, timestamps are stored in UTC format. On the frontend, I am implementing uib-timepicker for time editing and updating purposes. However, I do not want uib-timepicker to automatically convert the time from the server's timezone ...

Accessing data from a Class in Angular 2

I have developed a class to store the default settings for my application. However, I am unsure of how to retrieve data from this class and integrate it into my HTML. I attempted using an observable, but it doesn't seem to be effective. Here is a gli ...

How to bring in a class that was exported using `export = uuid` in Typescript

I'm facing a challenge while working with the node_module called uuid-js in TypeScript. After installing both the module and its typings, I am unsure how to properly import the module. Question: What is the correct way to import the module? The func ...

Data in Angular is not getting transmitted properly through the routing system

I have a situation where I am sending data through routing and fetching it. Here's the code snippet for sending the data: navigateWithState(x) { console.log(x); this.router.navigateByUrl('/full-layout/add-form/1', { query ...

Is it possible to import both type and value on the same line when isolatedModules=true?

Did you know with Typescript, you can do type-only imports? import type { Foo } from "./types" If the file exports both types and values, you can use two separate import statements like this: import type { Foo } from "./types"; import ...

Is it possible to effectively iterate through an Angular service request?

Can you help me troubleshoot this code to ensure it runs smoothly? onSubmit() { let pdfData = [ { field_name: 'data.Date', value: this.freshDeskData.date, placeholder: '', page_no: 1, }, ...

Error message: The value of ngIf has been altered after it has been checked

I am encountering the ngIf value changed error in my code and I'm unsure of the correct solution. Below is the HTML snippet causing the issue: <div *ngIf="currentVlaue"> <div class="section-body"> <div cla ...

"Encountered an issue with Next-Auth session returning as undefined in getServerSideProps using NextJS version 13.2

When inspecting the code below, session is found to be undefined upon logging from the client side after being transferred from getServerSideProps. import { getServerSession } from 'next-auth/next'; import { authOptions } from './api/auth/[. ...

The Angular Fire Firestore module does not include the 'FirestoreSettingsToken' in its list of exported members

When I initially compiled my project, this issue occurred. The error message displayed is as follows: Module '".../node_modules/@angular/fire/firestore/angular-fire-firestore"' has no exported member 'FirestoreSettingsToken' In my a ...

Here is a unique rewrite of the text: "Converting to TypeScript: Add the Node Modules Path to the global paths array in the 'module' module using

Utilizing vue ui, I initiated a vue project with typescript and subsequently integrated electron, iview, and less... After addressing the lexical issues in the *.ts files, running the vue-cli-service serve task in vue ui yielded the following output: Tot ...

What is the best way to refresh existing data retrieved by React Query without having to fetch everything again?

My current code structure requires me to refetch all the data after a successful mutation, as the client-side tasks are not updated automatically. Is there a way to update the tasks directly when I create or delete a task? const { data: sessionData } = ...

Guide on expanding the capabilities of IterableIterator in TypeScript

I am currently working on extending the functionality of Iterable by adding a where method, similar to C#'s Enumerable.where(). While it is straightforward to extend the Array prototype, I am encountering difficulties in figuring out how to extend an ...

The AngularJS 2 TypeScript application has been permanently relocated

https://i.stack.imgur.com/I3RVr.png Every time I attempt to launch my application, it throws multiple errors: The first error message reads as follows: SyntaxError: class is a reserved identifier in the class thumbnail Here's the snippet of code ...

npm encountered an abrupt conclusion of JSON input during parsing near "serify":"latest","cha"

After uninstalling angular-cli yesterday to update to @angular/cli, I encountered an error while trying to install @angular/cli: Received an unexpected end of JSON input while parsing near '...serify":"latest","cha' Even after attempting to c ...

Utilize @ngrx/store to merge various reducers from feature modules

I'm currently immersed in developing a test app to delve deeper into the @ngrx/store framework. Within this project, I've set up a module called TrainingModule that aims to store various exercises and related information. The code is functioning ...

Submitting the object in the correct format for the Firebase database

My goal is to structure the Firebase database in the following way: "thumbnails": { "72": "http://url.to.72px.thumbnail", "144": "http://url.to.144px.thumbnail" } However, I am struggling to correctly set the keys '72' and '144&apos ...

Strategies for avoiding the transformation of a union of tuples into a tuple of unions

Currently working on creating a function that can gracefully handle unexpected "failures". My initial thought was to go with a go-style function return, typing the return as [Val, null] | [null, Error]. However, I encountered an issue when trying to type- ...

In order to conceal the div tag once the animation concludes, I seek to implement React

I'm currently using the framer-motion library to add animation effects to my web page. I have a specific requirement where I want to hide a div tag used for animations once the animation is complete. Currently, after the animation finishes, the div t ...