Exploring ways to customize or replace the extended CurrencyPipe type in Angular

I'm currently working on reusing an existing currency pipe from Angular common. My objective is to truncate the .00 when the value is round. Here's the code I've come up with:

/** Transform currency string and round it.  */
@Pipe({name: 'customCurrency'})
export class CustomCurrencyPipe extends CurrencyPipe implements PipeTransform {
  transform(value: number|string|null|undefined): string|null {
    if (!isValue(value)) return null;
    const valueFormat = (+value % 1 === 0) ? '1.0-0' : '1.2-2';
    return super.transform(value, 'USD', 'symbol', valueFormat);
  }
}

function isValue(value: number|string|null|undefined): value is number|string {
  return !(value == null || value === '' || value !== value);
}

If I set the transform type to :any, it runs without any issues. However, I am restricted from using any in the current environment. Setting it to :string|null results in this error:

TS2416: Property 'transform' in type 'CustomCurrencyPipe' is not assignable to the same property in base type 'CurrencyPipe'.
  Type '(value: string | number | null | undefined) => string | null' is not assignable to type '{ (value: string | number, currencyCode?: string | undefined, display?: string | boolean | undefined, digitsInfo?: string | undefined, locale?: string | undefined): string | null; (value: null | undefined, currencyCode?: string | undefined, display?: string | ... 1 more ... | undefined, digitsInfo?: string | undefin...'.
    Type 'string | null' is not assignable to type 'null'.
      Type 'string' is not assignable to type 'null'.

7   transform(value: number|string|null|undefined): string|null {

Is there a way for me to set my return type so that it matches the signature of the extended pipe?

Answer №1

It appears that your code is actually correct. The Angular team has implemented stricter types in version 11, introducing overloads for some of the pipes used.

You can find more information in the source link: https://github.com/angular/angular/pull/37447

Therefore, the issue you are facing is related to a TypeScript compiler problem. To resolve it, consider implementing the overload as shown below:

@Pipe({ name: 'customCurrency' })
export class CustomCurrencyPipe extends CurrencyPipe implements PipeTransform {
  transform(value: number | string | null | undefined): null;
  transform(value: number | string | null | undefined): string | null {
    if (!isValue(value)) return null;
    const valueFormat = +value % 1 === 0 ? '1.0-0' : '1.2-2';
    return super.transform(value, 'USD', 'symbol', valueFormat);
  }
}

For a live example, check out this Stackblitz demo: Stackblitz

Answer №2

It is evident that violating Liskov's SOLID principle occurred due to the modification of the contract in the subclass. To resolve this issue, it is recommended to inject the currency pipe and utilize it as a service.

some-module.ts
...
providers: [CurrencyPipe],
....
customCurrency
@Pipe({name: 'customCurrency'})
export class CustomCurrencyPipe implements PipeTransform {
  constructor(private currencyPipe: CurrencyPipe) {}
  
  transform(value: number|string|null|undefined): string|null {
    if (!isValue(value)) return null;
    const valueFormat = (+value % 1 === 0) ? '1.0-0' : '1.2-2';
    return this.currencyPipe.transform(value, 'USD', 'symbol', valueFormat);
  }
  ....
}

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

Encountered an error while trying to install a package using NPM due to the requirement of 'require-from-string@

Every time I try to install a package (even nodejs), I encounter this issue. Here is what I have tried so far: Uninstalled all dependencies Cleared cache Reinstalled NPM / AngularCli Unfortunately, running any NPM command still results in the same error ...

Customizing Angular Forms: Set formcontrol value to a different value when selecting from autocomplete suggestions

How can I mask input for formControl name in HTML? When the autocomplete feature is displayed, only the airport's name is visible. After users select an airport, I want to show the airport's name in the input value but set the entire airport obje ...

The map pipe is malfunctioning

I am having trouble with displaying the key/value pairs of a map with a data type of <string, string[]>. Here is the custom pipe I am using: import { PipeTransform, Pipe } from "@angular/core"; @Pipe({ name: 'keys' }) export class KeysPi ...

Having trouble deploying my Angular application on Heroku

I have been attempting to deploy my Angular 6 application on Heroku. Despite building the project and following all deployment steps, I am encountering the following result on Heroku: https://i.sstatic.net/sIFGe.png Furthermore, the Heroku logs display t ...

How can I determine if a user has reached the end of a non-scrollable div by utilizing HostListener and directives within Angular?

In my Angular project, I have designed a main layout using flex display with specific height and overflow settings. The main content div inside this layout has its own unique styling to ensure consistent appearance for any components inserted within it. By ...

How can I effectively retrieve a value from Ionic storage and use it as an authorization token instead of receiving the promise "Bearer [object Promise]"?

In my Ionic 4 angular project, the storage.get('token').then() function returns a promise object instead of the refresh token. For JWT authentication, I send the access token as the bearer token in the authorization headers using an HTTP interce ...

Using template literals with Optional chaining in Javascript does not yield the expected results

Trying to implement template literal with optional chaining. type Item = { itemId:number, price: number}; type ItemType = { A:Item, B:Item }; const data : ItemType = { A:{itemId:1, price:2}, B:{itemId:2, price:3} }; let key = `data?.${variable}?.ite ...

Issue with importing in VueJS/TypeScript when using gRPC-Web

I have developed a straightforward VueJS application and am currently grappling with incorporating an example for file upload functionality. The proto file I am utilizing is as follows: syntax = "proto3"; message File { bytes content = 1; } ...

Tips for accessing a Literal type in TypeScript instead of a Union type

I recently developed a function to generate dynamic elements. However, I encountered an issue where instead of returning the precise type of the element, it was producing a union of various <HTMLElementTagNameMap> types. function createCustomElement( ...

Extending injections in Angular 5 by inheriting from a base class

I have created a class with the following structure: @Injectable FooService { constructor(protected _bar:BarService){ } } Then, I extended this class as shown below: @Injectable ExtFooService extends FooService { constructor(_bar:BarServi ...

Develop a duplication of the selected text without the need for the clipboard

Looking to implement an internal clipboard with a history feature for my application. Unfortunately, using the clipboard API will require user permission which is not feasible. I want to ensure that formatting such as bold, italics, and strikethrough is p ...

The Google Sign-in feature is unable to access the property 'load' due to being undefined

I'm currently working on implementing a Google Sign-in feature in an Angular application, but I'm encountering the following error: Cannot read property 'load' of undefined This was actually working perfectly just an hour ago, but n ...

modify style when not valid and is touched

When I try to modify the style of an i tag based on the touched and invalid state of another field, only the input tag's style changes and not the i tag. Here's the CSS code I'm using: input.ng-touched.ng-invalid { border-color: red; } ...

VSCode mistakenly detecting Sequelize findOne and findAll return type as any inferences

I have a model defined using Sequelize as shown below: import { Sequelize, Model, BuildOptions, DataTypes } from 'sequelize'; interface User extends Model { readonly id: string; email: string; name: string; password_hash: string; reado ...

Utilizing GraphQL Global Object Identification with NestJS using the code-first strategy

Currently, I am trying to incorporate Global Object Identification as outlined in the GraphQL documentation into my NestJS project. 1.) First, I created a Node interface: import { ID, InterfaceType, Field } from '@nestjs/graphql' @InterfaceType ...

iPhone glitch: When subscribing to an observable, I am immediately shown the initial value it emitted

Could you help me identify the issue in this code? I am attempting to set up data polling, but it seems that the responses after the initial request are not being sent to the back-end. Additionally, all observables return the same value as the first respon ...

When validation fails, all fields are highlighted in the Div containing the FormGroup

In my Angular application, I need to utilize two fields - produced date and expiry date. It is important to note that I must use <div [formGroup]...> since this component will be called within other forms. Using the form tag here is not an option. ...

Using an array of functions in Typescript: A simple guide

The code below shows that onResizeWindowHandles is currently of type any, but it should be an array of functions: export default class PageLayoutManager { private $Window: JQuery<Window>; private onResizeWindowHandlers: any; constructor () { ...

Could a class instance be transformed into an object that holds the keys of its public properties in the interface?

For example, if we have a Person object defined like this: class PersonClass implements Person { private _name : string; private _age : number; get name() : string {return this._name} get age() : number {return this._age} constructor(name : strin ...

"Enhance your web development with TypeScript and react-select

I find myself in a peculiar predicament. Currently, I am immersing myself in learning TypeScript and here is a brief overview of what transpired so far: const [selectedBankCode , setSelectedBankCode] = useState<string | undefined>(); const [selecte ...