Currently, I am in the process of refactoring an Angular application to enable strict typing.
One issue I have encountered is using array methods with an array union type in our LookupService. When attempting to call
const lookup = lookupConfig.find(l => l.code === code);
, an error pops up:
This expression is not callable. Each member of the union type '{ <S extends Lookup>(predicate: (this: void, value: Lookup, index: number, obj: Lookup[]) => value is S, thisArg?: any): S | undefined; (predicate: (value: Lookup<...>, index: number, obj: Lookup<...>[]) => unknown, thisArg?: any): Lookup<...> | undefined; } | { ...; } | { ...; }' has signatures, but none of those signatures are compatible with each other.
I've experimented with various solutions from Github and StackOverflow but haven't found a way to achieve this without creating new methods for each current and future union types as mentioned in this answer.
I've created a TS Playground which can be viewed below:
interface Lookup<T extends number | string = number> {
code: T;
name: string;
}
interface ProvinceLookup extends Lookup<string> {
countryCode: string;
}
interface LookupConfig {
college: Lookup[];
countries: Lookup<string>[];
provinces: ProvinceLookup[];
}
const lookupConfig: Lookup<number>[] | Lookup<string>[] | ProvinceLookup[] = [];
function getLookup<T extends number | string>(code: T) {
return lookupConfig.find((l: Lookup<T>) => l.code === code);
};
const result = getLookup(1);
The actual implementation where find
generates the error:
private lookupValue<T extends string | number>(
lookupCode: T,
lookupKey: string,
key: string
): unknown | null {
const lookupConfig = this.lookupService[lookupKey as keyof LookupConfig];
const lookup = lookupConfig?.find((l: Lookup) => l.code === lookupCode);
return lookup && Object.prototype.hasOwnProperty.call(lookup, key)
? lookup[key]
: null;
}