Я хочу определить функцию searchText
, которая принимает массив объектов, которые необходимо отфильтровать, массив свойств объекта, по которым будет выполняться поиск, и строковое значение, которое будет искать. В конце концов, я хочу, чтобы было возможно что-то подобное:
type User = { firstName: string, lastName: string, age: number}
let users: User[] = [
{ firstName: 'John', lastName: 'Smith', age: 22},
{ firstName: 'Ted', lastName: 'Johnson', age: 32}
]
searchText(users, ['firstName', 'lastName'], 'john') // should find both entries
Я хочу ограничить эту функцию, чтобы она принимала только допустимые массивы имен свойств. Допустимый массив должен содержать только свойства, которые имеют тип строки, поскольку функция ищет текст. В другом вопросе SO я нашел способ определить тип, который должен допускать только допустимые значения (https://stackoverflow.com/a/54520829/1242967). Этот тип определяется как
type KeysMatching<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T];
С помощью этого типа я могу определить функцию, которая позволяет указать «допустимое ограничение массива».
function searchTextInUsers(users: User[], stringFields: KeysMatching<User, string>[], text: string): User[]{
return users.filter(user =>
stringFields.some(field => user[field].toLowerCase().includes(text.toLowerCase()))
);
}
Это компилируется и работает, как ожидалось. Попытка передать свойство, не являющееся строкой, вызывает ошибку компиляции
searchTextInUsers(users, ['firstName', 'lastName', 'age'], 'john') // ERROR: Type 'string' is not assignable to type '"firstName" | "lastName"'.
Но на самом деле я хотел написать функцию, которая работала бы для любого типа, а не только User
, но, что удивительно, простое добавление параметра универсального типа не сработало.
function searchText<T>(elements: T[], stringFields: KeysMatching<T, string>[], text: string): T[]{
return elements.filter(element =>
stringFields.some(field => element[field].toLowerCase().includes(text.toLowerCase())) // Error on this line
);
}
Компилятор показывает следующую ошибку
Свойство toLowerCase не существует для типа T [{[K в ключе T]: T [K] расширяет строку? К: никогда; } [keyof T]] '
Может ли кто-нибудь объяснить, почему это не сработало и почему машинописный текст не сужает тип до строки в этом случае? Есть ли другой способ достичь того, что я хочу делать при использовании универсальных типов? Ссылка на игровую площадку