Reputation: 4471
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?
Upvotes: 1
Views: 1863
Reputation: 250336
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;
});
}
}
}
Upvotes: 1