I read about type guards in typescript here and here. But I still get compiler errors.
Error:(21, 14) TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ (callbackfn: (value: Foo, index: number, array: Foo...' has no compatible call signatures.
I got following classes:
Foo.ts
export class Foo {
expired: boolean;
}
Bar.ts
export class Bar {
foo: Foo;
}
MyPipe.ts
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'myPipe'
})
export class MyPipe implements PipeTransform {
transform(items: Foo[] | Bar[], isExpired: Boolean): Foo[] | Bar[] {
if (!items) {
return items;
}
if (items[0] instanceof Foo) {
return items.filter((foo: Foo) => {
return foo.expired == isExpired;
});
} else {
return items.filter((bar: Bar) => {
return bar.foo.expired == isExpired;
});
}
}
}
The question is, how can I achieve the use of union binding for my parameter "items" and the type guard usage at the same time in my angular pipe using typescript?
Typescript will generally not narrow the type of a variable based on the type of a field (with the exception of discriminated unions). More specifically typescript will do no narrowing based on array indexing (this is a known limitation)
The simplest thing you can do is use a type assertion, or the more elegant solution, a custom type guard:
class Foo { private x: string; expired: boolean }
class Bar { private x: string; foo: Foo }
function isArrayOf<T>(ctor: new (...args: any[]) => T, arr: any): arr is T[] {
return arr[0] instanceof ctor
}
export class MyPipe {
transform(items: Foo[] | Bar[], isExpired: Boolean): Foo[] | Bar[] {
if (!items) {
return items;
}
if (isArrayOf(Foo, items) {
return items.filter((foo: Foo) => {
return foo.expired == isExpired;
});
} else {
return items.filter((bar: Bar) => {
return bar.foo.expired == isExpired;
});
}
}
}