John Rousseau
John Rousseau

Reputation: 93

Passing generic Pipe that implements PipeTransform in Angular

I have a bunch of custom pipes like this:

@Pipe({ name: 'appPadThree', pure: true, standalone: true })
@Injectable()
export class PadThreePipe implements PipeTransform {
  transform(value: number): string {
    // Code
  }
}

I'm trying to write a generic function to use .transform() but .transform() doesn't exist on type Pipe.

Attempt 1:

  pipeData<T>(value: string, pipeName: T): string {
    const pipeWorker = inject(pipeName);
    return pipeWorker.transform(value);
  }

Attempt 2:

  pipeData<T extends PipeTransform>(value: string, pipeName: T): string {
    return pipeRef.transform(value);
  }

Error: Argument of type 'Pipe' is not assignable to parameter of type 'PipeTransform'. Property 'transform' is missing in type 'Pipe' but required in type 'PipeTransform'.

This code worked prior to the Angular 18 / standalone upgrade. It didn't use generics and was ugly.

  pipeData(value: any, pipeName: any): any {
    const pipeWorker = this.injector.get(pipeName);
    return pipeWorker.transform(value);
  }

How can I write a generic function that takes a Pipe as the type, knowing that it implements PipeTransform?

EDIT The caller gets a little complicated. I have an interface that looks like this:

export interface ITableViewer {
  ...
  displayPipe?: Pipe;
}

My object looks like this:

{ ... displayPipe: PadThreePipe },

And the caller:

this.pipeData(displayString, pipeName);

Upvotes: 0

Views: 105

Answers (1)

Yong Shun
Yong Shun

Reputation: 51485

Solution for Attempt 1

As you provide the PadThreePipe as type to the displayPipe field:

let pipeRef = { displayPipe: PadThreePipe };
let pipeName = pipeRef.displayPipe;

this.pipeData(displayString, pipeName)

You should refer to the class type by the constructor function. Reference: Generic Type Inference with Class Argument

pipeData<T>(value: string, pipeName: new () => T): string {
  const pipeWorker = inject(pipeName);
  return pipeWorker.transform(parseInt(value));
}

Solution for Attempt 2

Provide the PadThreePipe instance to the displayPipe field.

let pipeRef = { displayPipe: new PadThreePipe() };
let pipeName = pipeRef.displayPipe;

this.pipeData(displayString, pipeName);

Change the second argument name to pipeRef.

pipeData<T extends PipeTransform>(value: string, pipeRef: T): string {
  return pipeRef.transform(value);
}

Demo @ StackBlitz

Upvotes: 0

Related Questions