自定义反应挂钩的窄联合类型
Narrow union types for custom react hook
我有 4 个对象,它们有几个相似的键和几个不同的键。我正在使用他们的联合进行数据库操作,就像这样 ->
type Objects = Food | Diary | Plan | Recipe ;
分页自定义挂钩
function usePaginate (key: string, options: Options) {
const [data,setData] = useState<Objects[]>([]);
const perPage = 10;
const [first, setFirst] = useState(0);
const [last, setLast] = useState(perPage);
const links = () => data.slice(first,last);
function paginate(clicked: 'next' | 'previous') {
if(clicked === 'next' && data.length > (first+perPage)) {
setFirst(first+perPage);
setLast(last+perPage);
}
if(clicked === 'previous' && (first-perPage) >= 0) {
setFirst(first-perPage);
setLast(last-perPage);
}
}
useEffect(()=>{
setData(getData(key,options));
},[])
return [links(),paginate] as const;
}
在 StockDisplay 组件中调用自定义挂钩
const StockDisplay = () => {
const [foods, paginate] = usePaginate('foods',{'sort':['-stocked','info.name']});
return (<>{foods.map(food => <Stock data={food} key={food.id} />)}</>)
// I have also tried adding a check like below but the error persists. 'stocked' is a key that only exists in Food type
return (<>{('stocked' in foods) && foods.map(food => <Stock data={food} key={food.id} />)}</>)
}
此时,foods 的类型是 const foods: Objects[]
& paginate 的类型是 const paginate: (clicked: "next" | "previous") => void
。但是 return 行抛出这个错误 ->
Type 'Objects' is not assignable to type 'Food'.ts(2322)
Stock.tsx(12, 26): The expected type comes from property 'data' which is declared here on type 'IntrinsicAttributes & { data: Food; }'
这是因为在下面的 Stock 组件中,数据被定义为 Food
类型,但在上面的 StockDisplay 组件中,我收到 Objects[]
.
股票成分
const Stock = ({data} : {data: Food}) => {
return (
<></>
)
}
我的问题是,如何在 StockDisplay 组件中将钩子的 Objects[]
输出缩小到 Food[]
?
1.使用类型转换的快速解决方法
const StockDisplay = () => {
const [foods, paginate] = usePaginate('foods',{'sort':['-stocked','info.name']});
return (<>{(foods as Food[]).map(food => <Stock data={food} key={food.id} />)}</>)
// I have also tried adding a check like below but the error persists. 'stocked' is a key that only exists in Food type
return (<>{('stocked' in foods) && foods.map(food => <Stock data={food} key={food.id} />)}</>)
}
2。使用泛型
function usePaginate<T>(key: string, options: Options): readonly [T[], (clicked: 'next' | 'previous') => void] {
const [data,setData] = useState<Objects[]>([]);
const perPage = 10;
const [first, setFirst] = useState(0);
const [last, setLast] = useState(perPage);
const links = () => data.slice(first,last);
function paginate(clicked: 'next' | 'previous') {
if(clicked === 'next' && data.length > (first+perPage)) {
setFirst(first+perPage);
setLast(last+perPage);
}
if(clicked === 'previous' && (first-perPage) >= 0) {
setFirst(first-perPage);
setLast(last-perPage);
}
}
useEffect(()=>{
setData(getData(key,options));
},[])
return [links(),paginate] as const;
}
const StockDisplay = () => {
const [foods, paginate] = usePaginate<Food>('foods',{'sort':['-stocked','info.name']});
return (<>{foods.map(food => <Stock data={food} key={food.id} />)}</>)
// I have also tried adding a check like below but the error persists. 'stocked' is a key that only exists in Food type
return (<>{('stocked' in foods) && foods.map(food => <Stock data={food} key={food.id} />)}</>)
}
我有 4 个对象,它们有几个相似的键和几个不同的键。我正在使用他们的联合进行数据库操作,就像这样 ->
type Objects = Food | Diary | Plan | Recipe ;
分页自定义挂钩
function usePaginate (key: string, options: Options) {
const [data,setData] = useState<Objects[]>([]);
const perPage = 10;
const [first, setFirst] = useState(0);
const [last, setLast] = useState(perPage);
const links = () => data.slice(first,last);
function paginate(clicked: 'next' | 'previous') {
if(clicked === 'next' && data.length > (first+perPage)) {
setFirst(first+perPage);
setLast(last+perPage);
}
if(clicked === 'previous' && (first-perPage) >= 0) {
setFirst(first-perPage);
setLast(last-perPage);
}
}
useEffect(()=>{
setData(getData(key,options));
},[])
return [links(),paginate] as const;
}
在 StockDisplay 组件中调用自定义挂钩
const StockDisplay = () => {
const [foods, paginate] = usePaginate('foods',{'sort':['-stocked','info.name']});
return (<>{foods.map(food => <Stock data={food} key={food.id} />)}</>)
// I have also tried adding a check like below but the error persists. 'stocked' is a key that only exists in Food type
return (<>{('stocked' in foods) && foods.map(food => <Stock data={food} key={food.id} />)}</>)
}
此时,foods 的类型是 const foods: Objects[]
& paginate 的类型是 const paginate: (clicked: "next" | "previous") => void
。但是 return 行抛出这个错误 ->
Type 'Objects' is not assignable to type 'Food'.ts(2322)
Stock.tsx(12, 26): The expected type comes from property 'data' which is declared here on type 'IntrinsicAttributes & { data: Food; }'
这是因为在下面的 Stock 组件中,数据被定义为 Food
类型,但在上面的 StockDisplay 组件中,我收到 Objects[]
.
股票成分
const Stock = ({data} : {data: Food}) => {
return (
<></>
)
}
我的问题是,如何在 StockDisplay 组件中将钩子的 Objects[]
输出缩小到 Food[]
?
1.使用类型转换的快速解决方法
const StockDisplay = () => {
const [foods, paginate] = usePaginate('foods',{'sort':['-stocked','info.name']});
return (<>{(foods as Food[]).map(food => <Stock data={food} key={food.id} />)}</>)
// I have also tried adding a check like below but the error persists. 'stocked' is a key that only exists in Food type
return (<>{('stocked' in foods) && foods.map(food => <Stock data={food} key={food.id} />)}</>)
}
2。使用泛型
function usePaginate<T>(key: string, options: Options): readonly [T[], (clicked: 'next' | 'previous') => void] {
const [data,setData] = useState<Objects[]>([]);
const perPage = 10;
const [first, setFirst] = useState(0);
const [last, setLast] = useState(perPage);
const links = () => data.slice(first,last);
function paginate(clicked: 'next' | 'previous') {
if(clicked === 'next' && data.length > (first+perPage)) {
setFirst(first+perPage);
setLast(last+perPage);
}
if(clicked === 'previous' && (first-perPage) >= 0) {
setFirst(first-perPage);
setLast(last-perPage);
}
}
useEffect(()=>{
setData(getData(key,options));
},[])
return [links(),paginate] as const;
}
const StockDisplay = () => {
const [foods, paginate] = usePaginate<Food>('foods',{'sort':['-stocked','info.name']});
return (<>{foods.map(food => <Stock data={food} key={food.id} />)}</>)
// I have also tried adding a check like below but the error persists. 'stocked' is a key that only exists in Food type
return (<>{('stocked' in foods) && foods.map(food => <Stock data={food} key={food.id} />)}</>)
}