【发布时间】:2020-08-17 06:02:49
【问题描述】:
我想定义一个函数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
);
}
编译器显示以下错误
类型 'T[{ [K in keyof T] 上不存在属性 'toLowerCase': T[K] 扩展字符串? K:从不; }[keyof T]]'
有人可以解释为什么这不起作用以及为什么打字稿在这种情况下没有将类型缩小为字符串?使用泛型类型时,是否有另一种方法可以实现我想要做的事情? Playground link here
【问题讨论】:
标签: typescript typescript-generics mapped-types conditional-types