2

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?

eko
  • 39,722
  • 10
  • 72
  • 98
Luke
  • 386
  • 1
  • 4
  • 15

2 Answers2

5

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

eko
  • 39,722
  • 10
  • 72
  • 98
-1

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);
  }
  ....
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Andrei
  • 10,117
  • 13
  • 21