我正在尝试使用TypeScript编写自定义的React钩子。 我编写了简单的辅助函数,可以帮助我检查目标对象中是否存在这样的属性:
export function hasOwnProperty<X extends {}, Y extends PropertyKey>(
obj: X,
prop: Y,
): obj is X & Record<Y, unknown> {
return obj.hasOwnProperty(prop);
}
我想在钩子上使用它:
export const useAutocomplete = <T, Y extends PropertyKey>(data: Array<T>, property?: Y) => {
const [query, setQuery] = useState('');
const [results, setResults] = useState<T[]>([]);
const findResults = (element: T) => {
if (property && hasOwnProperty(element, property)) {
return String(element[property])
.toLowerCase()
.includes(query.toLowerCase());
}
return String(element).toLowerCase().includes(query.toLowerCase());
};
// ...other code
};
问题是我即使使用toLowerCase()
也无法使用if (typeof obj[property] === 'string')
等方法,但无法使用。这就是为什么我决定将对象包装在String中然后可以访问所需的方法的原因。但是我认为这不是一个好的解决方案,我想问一下解决这个问题的更好方法是什么?
答案 0 :(得分:0)
Typescript不知道什么是T
明确告诉打字稿您的T
是带有字符串值的对象
T extends { [key: string]: string}
完整代码
export const useAutocomplete = <T extends { [key: string]: string }, Y extends PropertyKey>(data: Array<T>, property?: Y) => {
const [query, setQuery] = useState('');
const [results, setResults] = useState<T[]>([]);
const findResults = (element: T) => {
if (property && hasOwnProperty(element, property)) {
return element[property]
.toLowerCase()
.includes(query.toLowerCase());
}
return String(element).toLowerCase().includes(query.toLowerCase());
};
// ...other code
};
答案 1 :(得分:0)
最后,我弄清楚了如何使它真正变得美观而灵活! 您可以留下您的评论,如果有的话,可以使其更简洁,简短和灵活。 在我的解决方案中,我使用了函数重载和部分类型:
export const useAutocomplete = <T>(
data: Array<T>,
property?: Partial<T>,
) => {
// Declaring state
const [query, setQuery] = useState('');
const [results, setResults] = useState<T[]>([]);
// Creating type
// for overloading function
// it will allow us to accept
// both simple string arrays
// and sort by specific properties
type Overloaded = {
(element: string): boolean;
(element: T): boolean;
};
// Simple helper function
const getValue = (value: string, target: string) => value
.toLowerCase()
.includes(target.toLowerCase());
// Getting keys
const getObjectKeys = (
criteria: Partial<Cocktail>,
) => Object.keys(criteria) as (keyof Cocktail)[];
const findResults: Overloaded = (element: any) => {
if (property) {
const propertyValue = getObjectKeys(property);
return propertyValue.every((value) => getValue(element[value], query));
}
return getValue(element, query);
};
const handleSearch = (e: React.SyntheticEvent) => {
const target = e.target as HTMLInputElement;
setQuery(target.value);
const searchResults = data.filter(findResults);
setResults(searchResults);
};
return { handleSearch, results, query };
};