How to overwrite / match extended CurrencyPipe method type in Angular

1.4k Views Asked by At

I am trying to reuse existing currency pipe from Angular common. Goal is to have .00 truncated when the value is round. For this I've wrote this piece of code:

/** 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 transform type to :any it runs no problem. However I am not allowed to use any in current environment. And if I set it to :string|null I get 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 {

How can I set my return type so it matches signature of extended pipe?

2

There are 2 best solutions below

0
eko On BEST ANSWER

Actually there's nothing wrong with your code. Angular team introduced stricter types in version 11 where some of the pipes have overloads.

Source: https://github.com/angular/angular/pull/37447

Thus, this is purely a typescript compiler issue. You can get rid of it by simply implementing the overload.

@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);
  }
}

Stackblitz

0
Andrei On

it is true because you've broken the Liskov's SOLID principle, by changing the contract in the extending class. instead you can inject the currency pipe and use 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);
  }
  ....
}