Type '{ userId: string; }' has no properties in common with type 'AxiosRequestConfig'. | Axios - Next.js with typescript - reactjs

( Hi comunnity ) I have this piece of code, everything was working fine, but got an error once i create the API.delete function, don't know what is going on there actually
import axios, { AxiosRequestConfig } from "axios";
const API = axios.create({ baseURL: "http://localhost:5000/api" });
// Set header for each request to give permission
API.interceptors.request.use((req: AxiosRequestConfig) => {
if (localStorage.getItem("Auth")) {
req.headers.Authorization = `Bearer ${
JSON.parse(localStorage.getItem("Auth")).token
}`;
}
return req;
});
// login - register - update perfil
export const login = (loginData: {
email: string | null;
password: string | null;
}) => API.post(`/user/login`, loginData);
export const register = (registerData: {
email: string;
password: string;
name: string;
}) => API.post("/user/register", registerData);
export const updatePerfilPhotos = (
photosBase64: { perfil?: string; banner?: string },
id: string
) => API.patch(`/user/update/${id}`, photosBase64);
export const AddNotification = (
userInformation: { userId: string },
id: string
) => API.patch(`/user/notification/${id}`, userInformation);
export const DeleteNotificationOrFriend = (
userInformation: { userId: string },
id: string
) => API.delete(`/user/deleteNotification/${id}`, userInformation);
//
In the API.delete function there's a problem :
(parameter) userInformation: {
userId: string;
}
Type '{ userId: string; }' has no properties in common with type 'AxiosRequestConfig'
What does that mean ? why is that happening, how can i fix this ?
Thanks for your time friends !

I think the delete method signature should be like this,
API.delete(`/user/deleteNotification/${id}`, { data: userInformation })
Refer: https://github.com/axios/axios/issues/897#issuecomment-343715381

Related

I have problem to render data after API fetch because I have two apis and the second api call depend on first api ID

App.tsx
export interface Hotel {
id: string;
name: string;
address1: string;
address2: string;
starRating: number;
images: string[];
longDescription: string;
}
// currently not using types but may be it should be like that
export interface Hotel {
id: string;
name: string;
address1: string;
address2: string;
starRating: number;
images: string[];
longDescription: string;
room: {
id: string;
name: string;
longDescription: string;
occupancy: {
maxAdults: number;
maxChildren: number;
maxOverall: number;
};
};
}
//
export interface Room {
id: string;
name: string;
longDescription: string;
}
const App: React.FC = () => {
// const [loading, setLoading] = useState<boolean>(false);
const [hotels, setHotels] = useState<Hotel[]>([]);
const [rooms, setRooms] = useState<Room[]>([]);
useEffect(() => {
const getData = async () => {
const { data: hotels } = await axios.get<Hotel[]>(
'https://obmng.dbm.guestline.net/api/hotels?collection-id=OBMNG'
);
const allRooms = await Promise.all(
hotels.map(async ({ id }) => {
// don't forget to provide a type for the response
const {
data: { rooms },
} = await axios.get<{ rooms: Room[] }>(
`https://obmng.dbm.guestline.net/api/roomRates/OBMNG/${id}`
);
return rooms;
})
);
console.log(hotels);
console.log(allRooms.flat());
setHotels(hotels);
setRooms(allRooms.flat());
};
getData();
}, []);
return (
<div>
<GlobalStyle />
<ImageWrapper>
<BrandTitle>Book Your Stay</BrandTitle>
<BrandSubtitle>make yourself comfortable with us</BrandSubtitle>
</ImageWrapper>
<HotelCard hotels={hotels} rooms={rooms} />-
</div>
);
};
HotelCard.tsx
interface HotelListProps {
hotels: Hotel[];
rooms: Room[];
}
const HotelCard: React.FC<HotelListProps> = ({ hotels, rooms }) => {
return (
<div>
{hotels.map((hotel) => (
<div key={hotel.id}>
<h1>{hotel.id}</h1>
// more code
</div>
))}
{rooms.map((room) => (
<div key={room.id}>
<h1>{room.name}</h1>
// more here...
</div>
))}
</div>
);
};
export default HotelCard;
In this code I get result like all hotels from first api and then I use id which I got from first api call and use that one in second api request. I stored both api response in different state(hotels, rooms). but i got different result in screen. I need to render like, first hotel1 then 3 rooms of hotel1 then again hotel2 and 3 rooms of hotel2 and rest of them same, but rightnow I got first hotel then all rooms together. how to achieve that? I am really confuse about that could anyone help me? I will really thankful for that?
First organize your data into the structure you want
const joined = hotels.map(h => {
hotel: h,
rooms: rooms.filter(r => r.hotelId === h.id), // Or however your docs are linked
});
// Then
joined.map(j => <>
<h1>{j.hotel.id}</h1>
{j.rooms.slice(0, 2).map(r => <h1>{r.id}</h1>)}
</>);

How to define typescript interface for array on objects

Here is the data from the server.
I want to display it with map on UI.
Here is the interface I did -
export interface IHistory {
reports: Readonly<{
readonly id?: string,
status?: Status,
readonly created_at?: Date,
}>
}[];
The map I'm doing:
{props.history!.map((index, idx) => {}
The error:
TypeError: Cannot read properties of null (reading 'map')
What am I doing wrong?
I want to display only the reports.
Added -
Interface -
export interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
};
const [ hitoryState, setHistoryState ] = useState<IHistory | null>(null);
useEffect(() => {
backendAPIAxios.get('/history')
.then((response: AxiosResponse<IHistoryResponse>) => {
if (!response.data) {
return alert('Failed to get history');
}
setHistoryState(() => response.data);
})
.catch((e: AxiosError) => {
// alert(`Failed to get history with error: ${e}`);
});
}, [setHistoryState])
console.log(props.history!.reports.map((hist) => <p>{hist.created_at}</p>))
This is the error I'm getting:
You are making IHistory an array of reports objects, when reports is the field with the array. Also, created_at will likely be a string and not a date if it's being returned from the backend.
type Status = "fair" | "unfair";
interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
};
const backendHistory: IHistory = {
reports: [
{ id: "123", status: "fair", created_at: new Date().toISOString() },
{ id: "456", status: "unfair", created_at: new Date().toISOString() },
]
};
const result = backendHistory.reports.map(({ id }, _idx) => id);
console.log("result", result);
React code:
import React from "react";
type Status = "fair" | "unfair";
interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
}
async function fakeFetch(): Promise<IHistory> {
const backendHistory: IHistory = {
reports: [
{ id: "123", status: "fair", created_at: new Date().toISOString() },
{ id: "456", status: "unfair", created_at: new Date().toISOString() }
]
};
return new Promise((resolve) =>
setTimeout(() => resolve(backendHistory), 1000)
);
}
export default function App() {
const [backendHistory, setBackendHistory] = React.useState<IHistory>();
React.useEffect(() => {
let isAlive = true;
(async function () {
const result = await fakeFetch();
if (isAlive) {
setBackendHistory(result);
}
})();
return () => {
isAlive = false;
};
}, []);
return (
<div className="App">
<h1>Backend History</h1>
{backendHistory ? (
backendHistory.reports.map((hist) => <p>{hist.id}</p>)
) : (
<span>loading</span>
)}
</div>
);
}

how can i give type in getServerSideProps of Nextjs with typescript?

I'm using NextJs + TypeScript to make a little clone project, but I got a problem with type in getServerSideProps.
As you can see, in getServerSideProps, I am fetching data using with context.query.
But some error message is not fixed and I don't understand why that error appears.
The error message is this.
Type 'string[]' cannot be used as an index type.ts(2538)
Type 'undefined' cannot be used as an index type.ts(2538)
const genre: string | string[] | undefined
How can I fix this type problem?
import Head from "next/head";
import Nav from "../components/Nav/Nav";
import Header from "../components/Header/Header";
import Results from "../components/Results/Results";
import requests from "../utils/requests";
import { GetServerSideProps } from "next";
type HomeProps = {
results: {}[];
};
export default function Home({ results }: HomeProps) {
console.log(results);
return (
<div>
<Results results={results} />
</div>
);
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const genre = context.query.genre
const response = await fetch(
`https://api.themoviedb.org/3${
requests[genre]?.url {/*this is problem line*/}
|| requests.fetchTopRated.url
}`
);
const data = await response.json();
return {
props: { results: data.results },
};
};
You can use like this;
type PageProps = {
isAuthanticated: boolean,
categories?: CategoryType[]
}
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
const _props: PageProps = {
isAuthanticated: auth,
categories: data.results
}
return { props: _props }
};
const Category: NextPage<PageProps> = (props) => {
return(
...
)
};
Since the type of genre can be string or string[] (or undefined), it can not be used to index requests without being narrowed down to string via the use of an if statement:
if (typeof genre === 'string') {
// Typescript now knows it is a string
const response = await fetch(
`https://api.themoviedb.org/3${
requests[genre]?.fetchTopRated.url {/*this is problem line*/}
|| requests.fetchTopRated.url
}`
);
const data = await response.json();
return {
props: { results: data.results },
};
} else if (typeof genre == 'object'){
// handle case where it is an array
} else {
// genre is undefined
}
When you receive params via context, the value could be either string or string[] (or undefined) so you need to cast. It could be a single genre or multiple genres in the URL.
?genre=film or ?genre=film&genre=music
For you case, simply cast as string:
const genre = context.query.genre as string;
UPDATE
As per your comments, the first issue that you raised in the question was actually about casting to string as above.
The second issue, which you should not actually be seeing and must be a TS or module config issue, is related to trying to accessing a key as string by index on your vanilla object exported from "../utils/requests";
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ fetchTrending: { title: string; url: string; }; ...
Your data object has literal key names:
// ../utils/requests
export default {
fetchTrending: {
title: "Trending",
url: /trending/all/week?api_key=${API_KEY}&language=en-US,
},
fetchTopRated: {
title: "Top Rated",
url: /movie/top_rated?api_key=${API_KEY}&language=en-US,
},
};
Rather define the type like this:
export interface IRequest {
[name: string]: {
title: string;
url: string;
};
}
const data: IRequest = {
fetchTrending: {
title: "Trending",
url: `/trending/all/week?api_key=${API_KEY}&language=en-US1`
},
fetchTopRated: {
title: "Top Rated",
url: `/movie/top_rated?api_key=${API_KEY}&language=en-US`
}
};
export default data;
or you could use a Record to have strongly typed keys:
type RequestNames = "fetchTrending" | "fetchTopRated";
export const records: Record<
RequestNames,
{
title: string;
url: string;
}
> = {
fetchTrending: {
title: "Trending",
url: `/trending/all/week?api_key=${API_KEY}&language=en-US1`
},
fetchTopRated: {
title: "Top Rated",
url: `/movie/top_rated?api_key=${API_KEY}&language=en-US`
}
};

React testing with Typescript: Passing array of objects as props

I am trying to pass an array of objects to mock component data for testing like so:
const mockPackage = {
id: '1232-1234-12321-12321',
name: 'Mock Package',
price: 8.32,
description: 'Mock description',
globalProduct: {
imageUrl: 'http://imageurl.com',
},
};
const mockPackageData = {
name: 'Package',
packages: [mockPackage],
};
beforeEach(() => {
component = render(
<SuiteContextProvider>
<PackageCard
showDetail={{ display: true, selectedIndex: 1, hideOthers: false }}
handleShowDetail={handleShowDetail}
packageData={mockPackageData}
/>
</SuiteContextProvider>,
);
});
However, I receive the following error:
The component that receives the data destructures the packageData like so:
export interface Package {
id: string;
name: string;
price: number;
description: string;
globalProduct: {
imageUrl: string;
};
}
export interface PackageData {
name: string;
packages: [];
}
type Props = {
packageData: PackageData;
handleShowDetail: (data: DefaultPackageProps) => void;
showDetail: {
display: boolean;
selectedIndex: number | null;
hideOthers: boolean;
};
};
const PackageCard: React.FC<Props> = ({ packageData, handleShowDetail, showDetail }: Props) => {
return (
<>
{packageData.packages.map((packageInfo: Package, index: number) => {
const {
id,
name,
price,
description,
globalProduct: { imageUrl },
} = packageInfo;
Your PackageData defintion should be
export interface PackageData {
name: string;
packages: Package[];
}
You current code packages: []; declares packages to must be an empty array that is why you get the type '0' error.

TypeScript React: Use an array of strings as an index and get values from state

import React, { useState } from 'react';
interface PropsInterface {
keys: string; // name,email,password
}
const Poster: React.FC<PropsInterface> = (props: PropsInterface) => {
const [state, setState] = useState({
name: 'jim',
email: 'jim#gmaail.com',
password: 'secret',
loading: false,
keys: props.keys.split(','), // [ name, email, password ]
});
const { keys } = state;
I need to somehow get the values for each of the keys and form a string that will end up looking like this
"name=jim&email=jim#gmaail.com&password=secret"
I thought I could simply use URLSearchParams() that quickly became a problem with TypeScript. My next thought was to loop over the keys and then get the corresponding values from the state
for (const i in keys) {
if (keys.hasOwnProperty(i)) {
console.log(state[keys[i]]);
}
}
which then gives me the error
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ auth: boolean; baseUrl: string; keys: string[]; method: string; }'.
No index signature with a parameter of type 'string' was found on type '{ auth: boolean; baseUrl: string; keys: string[]; method: string; }'.ts(7053)
Fundamentally, when you do state[keys[i]], you're using a string to index into state. If your state object's type doesn't have an index signature, TypeScript won't let you do that (without a type assertion)...
You can use a local type assertion, perhaps with an alias variable, instead:
const rawState = state as any as Record<string, string>;
for (const key of keys) {
console.log(rawState[key]);
}
(Also note using for-of to loop the array.)
Of course, that's disabling type checking for that loop...
Live on the playground
As I understand, it's maybe your solution to solve the problem.
import * as React from "react";
import { render } from "react-dom";
interface PropsInterface {
keys: string; // name,email,password
}
const Poster: React.FC<PropsInterface> = (props: PropsInterface) => {
const [state, setState] = React.useState({
name: 'jim',
email: 'jim#gmaail.com',
password: 'secret',
loading: false,
keys: props.keys.split(','), // [ name, email, password ]
});
const pairKeys = React.useMemo<string>(() => {
const order = ["name", "email", "password"];
const result: string[] = [];
state.keys.forEach((key: string, index) => {
result.push(`${order[index]}=${state[order[index]]}`)
});
return result.join("&")
}, [state.keys])
return (
<div>{JSON.stringify(pairKeys, null, 2)}</div>
)
}
render(<Poster keys="John,test#test.com,testpass" />, document.getElementById("root"));

Resources