How to resolve TS2345 compile error in React - javascript

I try to write setState method in React.
But compile error occurs.
I want to know how to solve this issue.
frontend: react/typesript
articleApi.tsx
import axios from 'axios';
import {Article} from '../articleData';
export const getSingleArticleFactory = (id: string) => {
const getSingleArticle = async (id: string) => {
try {
const response = await axios.get('/api/article/' + id);
if (response.status !== 200) {
throw new Error('Server Error');
}
const article: Article = response.data;
return article;
} catch (err) {
throw err;
}
};
return getSingleArticle;
};
Detail.tsx
//import
interface ArticleState {
article: Article;
redirect: boolean;
user: firebase.User | null;
}
class Detail extends React.Component<
RouteComponentProps<{id: string}>,
ArticleState
> {
constructor(props: RouteComponentProps<{id: string}>) {
super(props);
this.state = {
article: {
id: 0,
title: '',
content: '',
imageNames: [],
},
redirect: false,
user: null,
};
this.getArticle = this.getArticle.bind(this);
this.deleteArticle = this.deleteArticle.bind(this);
}
getArticle() {
this.setState({
//error occures here
article: api.getSingleArticleFactory(this.props.match.params.id);,
});
}
articleData.tsx
export interface Article {
id: number;
title: string;
content: string;
imageNames: ImageName[];
}
interface ImageName {
name: string;
}
I expect there is no compile error.
But the actual is not.
I want to know how to solve this issue.
const getSingleArticleFactory: (id: string) => (id: string) => Promise<Article>
Argument of type '{ article: (id: string) => Promise<Article>; }' is not assignable to parameter of type 'ArticleState | ((prevState: Readonly<ArticleState>, props: Readonly<RouteComponentProps<{ id: string; }, StaticContext, any>>) => ArticleState | Pick<ArticleState, "article"> | null) | Pick<...> | null'.
Type '{ article: (id: string) => Promise<Article>; }' is not assignable to type 'Pick<ArticleState, "article">'.
Types of property 'article' are incompatible.
Type '(id: string) => Promise<Article>' is missing the following properties from type 'Article': id, title, content, imageNamests(2345)

I fixed it in following way:
async getArticle() {
const getSingleArticle = api.getSingleArticleFactory();
const articleData = await getSingleArticle(this.props.match.params.id);
this.setState({
article: articleData,
});
}
export const getSingleArticleFactory = () => {
const getSingleArticle = async (id: string) => {
try {
const response = await axios.get('/api/article/' + id);
if (response.status !== 200) {
throw new Error('Server Error');
}
const article: Article = response.data;
return article;
} catch (err) {
throw err;
}
};
return getSingleArticle;
};

Related

Typescript Errors "Missing Properties, and is not assignable to parameter void

I am new to typescript and I'm trying to understand why I'm getting the following errors.
Type 'FindCursor<WithId>' is missing the following properties from type 'Topping': name, priceCents
and
Argument of type 'Promise' is not assignable to parameter of type 'void'
public async getToppingsData(ID: String): Promise<Topping> {
const data = await this.collection.find({ _id: ID });
if (data === null || data === undefined) {
throw new Error(`Could not find ${ID}`);
}
const toppingData: Topping = data;
return toToppingObject(toppingData);
}
public async getToppingsByIds(toppingIds: String[]): Promise<String[]> {
toppingIds.map((id) => {
const toppingArray = [];
toppingArray.push(this.getToppingsData(id));
const toppingNames = toppingArray.map((topping) => {
toppingNames.push(topping);
});
return toppingNames;
});
}
here is the toPizzaObject function
import { Document } from 'mongodb';
import { Pizza } from '../application/providers/pizzas/pizza.provider.types';
import { ObjectId } from 'bson';
interface PizzaDocument extends Document, Omit<Pizza, 'toppings' | 'id'> {
toppingIds: ObjectId[];
}
const toPizzaObject = (pizza: PizzaDocument): Pizza => {
return {
id: pizza._id.toHexString(),
name: pizza.name,
description: pizza.description,
toppings: pizza.toppingIds.map((toppingId) => toppingId.toHexString()),
imgSrc: pizza.imgSrc,
};
};
export { PizzaDocument, toPizzaObject };
You messed up your map(), your were returning nothing, hence the void. Also you were not handle the Promise correctly, the return type wasn't matching.
Here is a suggestion:
public async getToppingsByIds(toppingIds: String[]): Promise<String[]> {
const toppingArray = [];
for (let id of toppingIds) {
const data = await this.getToppingsData(id);
toppingArray.push(data);
}
const toppingNames = toppingArray.map((topping) => {
return topping
});
return toppingNames
}
PS: await can't be used on map/forEach(), that's why there is a traditional for-loop.

If the mysql result value is an array and generic T is not an array, can it be removed in typescript?

type User = {
name: string
email: string
}
This is my code,
import type { PoolConnection, RowDataPacket, OkPacket } from "mysql2/promise";
type dbDefaults = RowDataPacket[] | RowDataPacket[][] | OkPacket | OkPacket[];
type dbQuery<T> = T & dbDefaults;
type FlattenIfArray<T> = T extends (infer R)[] ? R : T;
function isFlattenArray<T>(rows: any[]): rows is T[] {
return rows.length < 1;
}
// eslint-disable-next-line consistent-return
export async function queryWrapper<T>(
{ query, values }: { query: string; values?: string[] },
conn: PoolConnection
): Promise<T | undefined> {
try {
await conn.beginTransaction();
const [rows, _] = await conn.query<dbQuery<T>>(query, values || undefined);
await conn.commit();
if (isFlattenArray<FlattenIfArray<T>>(rows)) {
return rows;
}
return rows;
} catch (error) {
await conn.rollback();
} finally {
conn.release();
}
}
Mysql code only returns the array
There is no problem with User[].
But When using User, I want to remove the arrangement.
So I used this code, but it didn't work. What should I do?
I added additional explanation.
const result = queryWrapper<User>(query, values, conn)
When use user, It comes out like this.
[
{
name: "user-name",
email: "user#gmail.com"
}
]
But I want it comes out like this.
{
name: "user-name",
email: "user#gmail.com"
}
I'll reply by myself.
type dbDefaults =
| RowDataPacket[]
| RowDataPacket[][]
| OkPacket
| OkPacket[]
| ResultSetHeader;
type Query = { query: string; values?: any[] };
type QueryFunction<T = any> = () => Promise<[T & dbDefaults, FieldPacket[]]>;
type AlwaysArray<T> = T extends (infer R)[] ? R[] : T[];
// eslint-disable-next-line consistent-return
export async function queryTransactionWrapper<T = any>(
queries: QueryFunction[],
conn: PoolConnection
): Promise<[AlwaysArray<T>, FieldPacket[]][] | undefined> {
try {
await conn.beginTransaction();
// await conn.query("START TRANSACTION;");
const executedQueries = await Promise.all(
queries.map((query) => {
return query();
})
);
// await conn.query("COMMIT;");
await conn.commit();
return executedQueries;
} catch (error) {
logger.error(colors.blue(JSON.stringify(error)));
await conn.rollback();
} finally {
conn.release();
}
}
export function findOne({ query, values }: Query, conn: PoolConnection) {
return function () {
return conn.query<RowDataPacket[]>(query, values);
};
}
export function find({ query, values }: Query, conn: PoolConnection) {
return function () {
return conn.query<RowDataPacket[]>(query, values);
};
}
export function update({ query, values }: Query, conn: PoolConnection) {
return function () {
return conn.query<ResultSetHeader>(query, values);
};
}
export function insert({ query, values }: Query, conn: PoolConnection) {
return function () {
return conn.query<OkPacket>(query, values);
};
}
when call findOneUser
async findByEmail(email: string): Promise<IUser | undefined> {
const conn = await this.mysql.getConnection();
const query = `Select * FROM ${USER_TABLE} WHERE email=?`;
const findUserQueryFunction = findOne({ query, values: [email] }, conn);
const executedQueries = await queryTransactionWrapper<IUser>(
[findUserQueryFunction],
conn
);
if (!executedQueries) {
throw new Error();
}
const [[rows]] = executedQueries;
return rows[0];
}
if <IUser[]>, findByEmail returns rows
else returns rows

Problem handling Promises with Typescript

I have a small problem deleting with types. Here is my code snippet where I have the issue:
const prams = useParams<RouteParams>();
const [event, setEvent] = useState<EventType>();
const [error, setError] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
let id = prams.id;
console.log(id);
useEffect(() => {
setLoading(true);
if (id) {
getEventById(id)
.then((res: EventType) => { // the issue is here it says is not assignable to parameter of type '(value: unknown) => void | PromiseLike<void>'
setEvent(res);
console.log(res, "rs");
setLoading(false);
})
.catch((err) => {
setError(true);
setLoading(false);
});
}
}, [id]);
console.log(event, "events");
When I replace Event with any it works fine.
the function for request the Event
import axios from "axios";
import { Config, URL } from "./Api";
const getEventById = async (id: string) => {
const { data } = await axios.get(`${URL}getEvent/${id}`, Config);
return Object.values(data?.event)[0];
};
export { getEventById };
Here is the Event Type:
export type EventType = {
ADDRESS: string;
EVENT_DATE: string;
ID: string;
MIN_PRICE: number;
NAME: string;
PICTURE: string;
PLACE: string;
PREVIEW: string;
REGISTRATION_END_DATE: string;
TYPE: string;
DESCRIPTION: string;
RESTAURANT_LOGO: string;
RESTAURANT_ID?: string;
};
and here is the response from the getEventById():
Any clue where the problem is, or maybe the solution?
Well, your getEventById doesn't declare a proper return type, so it does not return a Promise<Event> but a Promise<any> because there is no way, the TS compiler can infer the correct type.
The simplest would probably be
const getEventById = async (id: string): Promise<Event> => {
const { data } = await axios.get(`${URL}getEvent/${id}`, Config);
return Object.values(data?.event)[0];
};
Here Event is representing the Keyboard event. You need a different interface relevant to the response from the service. Try renaming it and see if you are importing the right interface

Using Typescript generic types

I have an interface that describes basic database CRUD operations like so:
export interface IUserDB {
insert: (params: UserAttributes) => Promise<UserResult>
findById: ({ id }: { id: number }) => Promise<UserResult | null>
findByEmail: ({ email }: { email: string }) => Promise<UserResult | null>
remove: ({ id }: { id: number }) => Promise<any>
update: ({
id,
...changes
}: { id: number } & UserAttributes) => Promise<UserResult>
}
I then go ahead to use this interface in this manner:
const makeAddUser = ({ usersDb }: { usersDb: IUserDb }) => {
return async function addUser(userDetails: User) {
const user = makeUser(userDetails)
const exists = await usersDb.findByEmail({ email: user.getEmail() })
if (exists) {
throw new UniqueConstraintError('Email')
}
const newUser = await usersDb.insert({
name: user.getName(),
email: user.getEmail(),
password: user.getPassword(),
username: user.getUsername(),
})
const { id } = newUser.user
await publisher(id.toString(), 'newuser.verify')
await consumer('verify_queue', verifyUser, '*.verify')
return newUser
}
}
I want to be able to reuse this interface for PostDb and CommentDb without me having to write another interface that describes a Post database operation, how can I achieve this? I read about Typescript Generics, but I am not really sure how to apply it to this case, any help will be appreciated. I am still trying to get a hang of Typescript. Thank you very much!
Not really sure how to explain it other than to just show you, but here is what I would do (with some filler code to make things work, but just focus on the interfaces). But basically you define an interface, and pass in the types you want it to support inside <>. So below I do
export interface IDB<Attributes, Result> {
which means you pass in two additional types to this:
interface IUserDb extends IDB<UserAttributes, UserResult>
and now IUserDb is an IDB where any place IDB has Attributes it instead uses UserAttributes, and same with Result and UserResult:
https://codesandbox.io/s/typescript-playground-export-forked-nej0s?file=/index.ts:0-1569
export interface IDB<Attributes, Result> {
insert: (params: Omit<Attributes, 'id'>) => Promise<Result>
findById: ({ id }: { id: number }) => Promise<Result | null>
remove: ({ id }: { id: number }) => Promise<any>
update: ({
id,
...changes
}: { id: number } & Attributes) => Promise<Result>
}
interface UserAttributes {
id: string;
name: string;
username: string;
email: string;
password: string;
}
interface UserResult extends UserAttributes {
getEmail(): string
getName(): string
getPassword(): string
getUsername(): string
}
export interface IUserDb extends IDB<UserAttributes, UserResult>{
findByEmail: ({ email }: { email: string }) => Promise<UserResult | null>
}
const makeUser = (user: UserAttributes): UserResult => {
const u: any = { ...user };
u.getName = () => u.name;
u.getEmail = () => u.email;
u.getPassword = () => u.password;
u.getUsername = () => u.username;
return u as UserResult;
}
const UniqueConstraintError = (err: string) => new Error(`Unique Constraint Error: ${err}`);
const makeAddUser = ({ usersDb }: { usersDb: IUserDb }) => {
return async function addUser(userDetails: UserAttributes) {
const user = makeUser(userDetails)
const exists = await usersDb.findByEmail({ email: user.getEmail() })
if (exists) {
throw UniqueConstraintError('Email')
}
const newUser = await usersDb.insert({
name: user.getName(),
email: user.getEmail(),
password: user.getPassword(),
username: user.getUsername(),
})
return newUser
}
}

Property `data` does not exist on Type <void> | AxiosHttpResponse<any>

I have a promise, that I use in order to setState, when I fetch Data for a specific User. Below is the code for that:
getUserUsername = (): string => {
const { match } = this.props;
return match.params.username;
};
onFetchUser = () =>
getUser(this.getUserUsername())
.then(username => {
if (this.hasBeenMounted) {
this.setState({
user: username.data // The error is here.
});
}
})
.catch((errorResponse: HttpResponseObj | null = null) => {
if (this.hasBeenMounted) {
this.setState({
isLoading: false,
user: null,
errorMessage: errorResponse
});
}
});
But I get this TS error saying:
Property 'data' does not exist on type 'void | AxiosResponse<IUser>'.
Property 'data' does not exist on type 'void'.ts(2339)
---
any
The getUser(), is a service that I use and the code for it is here:
export const getUser = (username: string, initialOptions = {}): HttpResponse<IUser> => {
const options = {
method: httpMethod.GET,
url: endpoint.GET_USER(username)
};
return Instance(options, lensesOptions);
};
The code for the HttpResponse is here:
export interface HttpResponse<T> extends Promise<void | AxiosResponse<T>> {}
I tried something like:
.then((username): HttpResponse<any> => { // Doesn't work though
if (this.hasBeenMounted) {
this.setState({
user: username.data
});
}
})
Here is the Axios Interface:
export interface AxiosResponse<T = any> {
data: T;
status: number;
statusText: string;
headers: any;
config: AxiosRequestConfig;
request?: any;
}
Can you please explain to me what is the problem. I go to axios Interface and I see it data, as well as the generic there no problem.. Thank you!!
You have to check the type of username before try to use it because the function is returning two values with different properties (void and AxiosResponse)
So you have to check like this:
.then(username => {
// Check if it is not void
if (this.hasBeenMounted && username ) {
this.setState({
user: username.data
});
}
})

Categories

Resources