My Code
import * as R from 'ramda';
import { ILPAsset } from 'shared/types/models';
interface TextMatchFunction {
(part: string, typed: string): boolean;
}
const textMatch: TextMatchFunction = (part: string, typed: string) => typed.search(part) !== -1;
export const filterAssets = (txt: string, assets: ILPAsset[]): ILPAsset[] => {
const checkText = (k: string, c: keyof ILPAsset) => (textMatch(txt, c[k].toLowerCase()) ? c : null);
const curriedCheckText = R.curry(checkText);
// @ts-ignore
const bySymbol = R.map(curriedCheckText('symbol'), assets);
return R.reject(R.isNil, bySymbol);
};
IPAsset's interface
export interface ILPAsset {
symbol: string;
lastPayout: number;
historical: number;
}
Problem is on this line:
const checkText = (k: string, c: keyof ILPAsset) => (textMatch(txt, c[k].toLowerCase()) ? c : null);
Typescript expects k to be a number c[k]
, when it's in fact a key for an object in ILPAsset, which is string that in my case will be symbol
.
How would this be handled in Typescript?
A much simpler approach to do this btw, however I got a great answer for future issues in regards to key checking :D
export const filterAssets = (typed: string, assets: ILPAsset[]): ILPAsset[] => {
const checkSymbol = (asset: ILPAsset) =>
asset.symbol.includes(typed.toUpperCase());
return R.filter(checkSymbol, assets);
};
The problem is caused because you are using k
as the key to c
. Since you mention you expect k
to be a keyof ILPAsset
that would mean c
should be ILPAsset
. So the signature should be:
const checkText = (k: keyof ILPAsset, c: ILPAsset) => (textMatch(txt, c[k].toLowerCase()) ? c : null);
The left over problem is that now the index access c[k]
will not be of type string
since ILPAsset
contains both number
and string
keys.
We have two solutions for this.
We could check if c[k]
is a string
and if it not return null
:
const checkText = (k: keyof ILPAsset, c: ILPAsset) => {
const v = c[k];
return typeof v === 'string' ? (textMatch(txt, v.toLowerCase()) ? c : null): null;
}
We could also filter the keys so k
can only be a key taht would be a string
type StringKeys<T> = { [P in keyof T] : T[P] extends string ? P: never}[keyof T]
const checkText = (k: StringKeys<ILPAsset>, c: ILPAsset) => (textMatch(txt, c[k].toLowerCase()) ? c : null);
Note: The only string
key of ILPAsset
is symbol
so perhaps you should evaluate the need for the k
parameter at all. Why not just access c.symbol
?