Using "use-places-autocomplete" hook with Material-UI's Autocomplete? - reactjs

I'm trying to use this hook along with Material-UI's Autocomplete component, but am not having much success. Does anyone have an example of this scenario?
I receive a TS error for the value prop, stating
Type 'string' is not assignable to type 'AutocompletePrediction | null | undefined'.
The main issue though is that when I type into the input, results aren't being displayed for some reason, and neither the handleSelect nor the handleInput methods are being triggered.
Here's what I have so far:
import { useEffect, useRef, useState } from 'react';
import {
ClickAwayListener,
Grid,
TextField,
Typography,
} from '#material-ui/core';
import LocationOnIcon from '#material-ui/icons/LocationOn';
import parse from 'autosuggest-highlight/parse';
import Autocomplete from '#material-ui/lab/Autocomplete';
import usePlacesAutocomplete, {
getGeocode,
getLatLng,
} from 'use-places-autocomplete';
interface Props {
onSelect: (value: string) => void;
}
const EditLocation: React.FC<Props> = ({ onSelect }) => {
const {
ready,
value,
suggestions: { status, data },
setValue,
clearSuggestions,
} = usePlacesAutocomplete({
debounce: 300,
});
const handleInput = e => {
setValue(e.target.value);
};
const handleSelect =
({ description }: { description: string }) =>
() => {
console.log({ description });
// When user selects a place, we can replace the keyword without request data from API
// by setting the second parameter to "false"
setValue(description, false);
clearSuggestions();
// Get latitude and longitude via utility functions
getGeocode({ address: description })
.then(results => getLatLng(results[0]))
.then(({ lat, lng }) => {
console.log('📍 Coordinates: ', { lat, lng });
})
.catch(error => {
console.log('😱 Error: ', error);
});
};
return (
<ClickAwayListener onClickAway={() => clearSuggestions()}>
<Autocomplete
style={{ width: 300 }}
getOptionLabel={option =>
typeof option === 'string' ? option : option.description
}
filterOptions={x => x}
options={data}
autoComplete
includeInputInList
filterSelectedOptions
value={value} // <-- TS Error: "Type 'string' is not assignable to type 'AutocompletePrediction | null | undefined'."
onChange={handleInput}
renderInput={params => (
<TextField
{...params}
size="small"
label="Trip location"
variant="outlined"
fullWidth
/>
)}
renderOption={option => {
const matches =
option.structured_formatting.main_text_matched_substrings;
const parts = parse(
option.structured_formatting.main_text,
matches.map(match => [match.offset, match.offset + match.length])
);
return (
<Grid
container
alignItems="center"
onClick={() => handleSelect(option)}>
<Grid item>
<LocationOnIcon />
</Grid>
<Grid item xs>
{parts.map((part, index) => (
<span
key={index}
style={{ fontWeight: part.highlight ? 700 : 400 }}>
{part.text}
</span>
))}
<Typography variant="body2" color="textSecondary">
{option.structured_formatting.secondary_text}
</Typography>
</Grid>
</Grid>
);
}}
/>
</ClickAwayListener>
);
};
export default EditLocation;

The value property returned by usePlacesAutocomplete has type string while the data has type Suggestion[]. In order to make the Autocomplete stop complaining about the error, you need to pass a value with Suggestion type to the value prop of Autocomplete:
Autocomplete prop types: 1, 2.
<Autocomplete
options={data}
value={data.find(x => x.description === value)}

Related

Type 'HTMLInputElement | HTMLTextAreaElement' is not assignable to type 'HTMLInputElement'

I'm facing the following issue with updating the state using hooks. I want to update the todo with event.target.value, but error is raising.
Error: I'm facing the following issue with updating the state using hooks. I want to update the todo with event.target.value, but error is raising.
TS2345: Argument of type 'ChangeEvent<HTMLInputElement | HTMLTextAreaElement>' is not assignable to parameter of type 'ChangeEvent<HTMLInputElement>'.
Type 'HTMLInputElement | HTMLTextAreaElement' is not assignable to type 'HTMLInputElement'.
Type 'HTMLTextAreaElement' is missing the following properties from type 'HTMLInputElement': accept, align, alt, capture, and 26 more.
55 | variant="filled"
56 | style={{ width: "30rem" }}
> 57 | onChange={e => updateHandler(e, todo.id)}
| ^
58 | />
59 | ) : (
60 | <ListItemText
Code is as following:
import React, { useState } from "react";
import DeleteIcon from "#mui/icons-material/Delete";
import EditIcon from "#mui/icons-material/Edit";
import DoneIcon from "#mui/icons-material/Done";
import { ListItem, ListItemText, IconButton, TextField } from "#mui/material";
import { TodoModel } from "../Model";
type Props = {
todo: TodoModel;
todos: TodoModel[];
setTodos: React.Dispatch<React.SetStateAction<TodoModel[]>>;
};
const Todo = ({ todo, setTodos, todos }: Props) => {
const [edit, setEdit] = useState<boolean>(false);
const [editTodo, setEditTodo] = useState<string>(todo.todo);
const markDone = (id: number) => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, isDone: !todo.isDone } : todo
)
);
};
const updateTodo = (id: number) => {
if (!edit && !todo.isDone) {
setEdit(!edit);
}
};
const updateHandler = (
e: React.ChangeEvent<HTMLInputElement>,
id: number
) => {
setEditTodo(e.target.value);
setTodos(prevTodos => {
return prevTodos.map(todo =>
todo.id === id ? { ...todo, todo: editTodo } : todo
);
});
};
const removeTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<ListItem>
{edit ? (
<TextField
type="string"
value={editTodo}
variant="filled"
style={{ width: "30rem" }}
onChange={e => updateHandler(e, todo.id)}
/>
) : (
<ListItemText
style={{
color: "#f1f1ef",
width: "30rem",
textDecoration: `${todo.isDone ? "line-through" : "none"}`,
}}
primary={todo.todo}
/>
)}
<IconButton onClick={() => markDone(todo.id)}>
<DoneIcon />
</IconButton>
<IconButton onClick={() => updateTodo(todo.id)}>
<EditIcon />
</IconButton>
<IconButton onClick={() => removeTodo(todo.id)}>
<DeleteIcon />
</IconButton>
</ListItem>
</div>
);
};
export default Todo;
You need to extend type for event in updateHandler arguments, to be e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>

MUI Autocomplete with axios call to select or add new option [ codesandbox included ]

I'm trying to make an Autocomplete input for categories from an API response and allow the user to be able to create one if he didn't find
a match.
Issues:
1- How to avoid Non-unique when I have same key which is name can I make on ID cause it's unique?
Warning: Encountered two children with the same key, `Flour & Bread Mixes`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behaviour is unsupported and could change in a future version.
2- The dialog for entring new category doesn't open and I don't see any errors in the console
Code Sandbox
https://codesandbox.io/s/asynchronous-material-demo-forked-70eff?file=/demo.js
import React, {useState} from "react";
import TextField from '#mui/material/TextField';
import Autocomplete , { createFilterOptions } from '#mui/material/Autocomplete';
import CircularProgress from '#mui/material/CircularProgress';
import parse from 'autosuggest-highlight/parse';
import match from 'autosuggest-highlight/match';
import axios from "axios";
import Dialog from '#mui/material/Dialog';
import DialogTitle from '#mui/material/DialogTitle';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogActions from '#mui/material/DialogActions';
import Button from '#mui/material/Button';
import { Input } from "#material-ui/core";
export default function Asynchronous() {
const filter = createFilterOptions();
const [open, setOpen] = React.useState(false);
const [options, setOptions] = React.useState([]);
const loading = open && options.length === 0;
const [categories, setCategories] = useState([]);
const [openDialog, toggleOpenDialog] = React.useState(false);
const handleClose = () => {
setDialogValue({
name: '',
slug: '',
image: '',
});
toggleOpenDialog(false);
};
const handleSubmit = (event) => {
event.preventDefault();
setCategories({
name: dialogValue.name,
slug: dialogValue.slug,
image: dialogValue.image
});
handleClose();
};
const [dialogValue, setDialogValue] = React.useState({
name: '',
slug: '',
image: '',
});
React.useEffect(() => {
let active = true;
if (!loading) {
return undefined;
}
(async () => {
var config = {
method: 'get',
url: 'https://null.free.mockoapp.net/getCategories',
};
axios(config)
.then(function (response) {
response.data = response.data.filter((category) => category.name)
if (active) {
setOptions([...response.data]);
}
})
.catch(function (error) {
console.log(error);
});
})();
return () => {
active = false;
};
}, [loading]);
React.useEffect(() => {
if (!open) {
setOptions([]);
}
}, [open]);
return (
<>
<Autocomplete
id="asynchronous-demo"
open={open}
limitTags={3}
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
isOptionEqualToValue={(option, value) => option.name === value.name}
getOptionLabel={(option) => {
// Value selected with enter, right from the input
if (typeof option === 'string') {
return option;
}
// Add "xxx" option created dynamically
if (option.inputValue) {
return option.inputValue;
}
// Regular option
return option.name;
}}
options={options}
loading={loading}
multiple
renderInput={(params) => (
<>
<TextField
{...params}
label="Asynchronous"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
{console.log(options)}
</>
)}
renderOption={(props, option, { inputValue }) => {
const matches = match(option.name, inputValue);
const parts = parse(option.name, matches);
return (
<li {...props}>
<div>
{parts.map((part, index) => (
<span
key={index}
style={{
color: part.highlight ? "red" : 'inherit',
fontWeight: part.highlight ? 700 : 400,
}}
>
{part.text}
</span>
))}
</div>
</li>
);
}}
value={categories}
onChange={(event, newValue) => {
if (typeof newValue === 'string') {
// timeout to avoid instant validation of the dialog's form.
setTimeout(() => {
toggleOpenDialog(true);
setDialogValue({
name: newValue,
slug: '',
image: ''
});
});
} else if (newValue && newValue.inputValue) {
toggleOpenDialog(true);
setDialogValue({
name: newValue.inputValue,
slug: '',
image: ''
});
} else {
setCategories(newValue);
}
}}
filterOptions={(options, params) => {
const filtered = filter(options, params);
const { inputValue } = params;
const isExisting = options.some((option) => inputValue === option.name);
if (inputValue !== '' && !isExisting) {
filtered.push({
inputValue:inputValue,
name: `Add "${inputValue}"`,
});
}
return filtered;
}}
selectOnFocus
clearOnBlur
handleHomeEndKeys
/>
<Dialog open={openDialog} onClose={handleClose}>
<form onSubmit={handleSubmit}>
<DialogTitle>Add a new film</DialogTitle>
<DialogContent>
<DialogContentText>
Did you miss any film in our list? Please, add it!
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
value={dialogValue.name}
onChange={(event) =>
setDialogValue({
...dialogValue,
name: event.target.value,
})
}
label="title"
type="text"
variant="standard"
/>
<TextField
margin="dense"
id="slug"
value={dialogValue.slug}
onChange={(event) =>
setDialogValue({
...dialogValue,
slug: event.target.value,
})
}
label="slug"
type="text"
variant="standard"
/>
<Input
margin="dense"
id="image"
value={dialogValue.image}
onChange={(event) =>
setDialogValue({
...dialogValue,
image: event.target.value,
})
}
label="image"
type="file"
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button type="submit">Add</Button>
</DialogActions>
</form>
</Dialog>
</>
);
}
I found a lot of mistakes in your code so I made a new working fork on Codesandbox
https://codesandbox.io/s/asynchronous-material-demo-forked-oeg2p?file=/demo.js
Notes:
save the api response in a state instead of doing an API call
whenever the user opens the dropdown.
you could make an API request in handleSubmit function to create a new category in the back-end
handleFormSubmit will output the value of Autocomplete.
Let me know if you have any questions.
Read more about autocomplete at
https://mui.com/components/autocomplete/#asynchronous-requests

How to implement Formik's Field component with Material-Ui Autocomplete for Multi-Select check boxes?

I am trying to implement Formik's Field component and Material-Ui Autocomplete for Multiple values with checkboxs.
Every time I try to select a value from the dropdown list it closes the popup and again I have to open it for next value selection.
I am using setFieldValue() method to update the field value because of the Autocomplete's onChange behaviour.
import React, { useState, useEffect } from "react";
import { ErrorMessage, useField, Field, useFormikContext } from "formik";
import styles from "./Form.module.scss";
import { makeStyles, TextField, Checkbox } from "#material-ui/core";
import { Autocomplete } from "#material-ui/lab";
import service from "http-service";
import CheckBoxOutlineBlankIcon from "#material-ui/icons/CheckBoxOutlineBlank";
import CheckBoxIcon from "#material-ui/icons/CheckBox";
// Interface
interface IProps {
name: string;
rest?: any;
className?: string;
marginBottom?: string;
label?: string;
options?: any[];
disabled?: boolean;
tabIndex?: number;
multiple?: boolean;
returnArray?: boolean;
referentialIdKey?: string;
referentialNameKey?: string;
serviceConfig?: string;
query?: string;
processResponse?: Function;
}
// Custom Style
const useStyles = makeStyles((theme) => ({
root: {
"& .MuiAutocomplete-inputRoot": {
padding: "6px",
},
},
}));
const AsyncSearchMultiSelect: React.FC<IProps> = (props) => {
const {
name,
className,
disabled,
options,
marginBottom,
label,
tabIndex,
multiple,
returnArray,
referentialIdKey,
referentialNameKey,
serviceConfig,
query,
processResponse,
...rest
} = props;
const { setFieldValue } = useFormikContext();
const [field] = useField(name);
const classes = useStyles();
// States :
const [asyncOptions, setAsyncOptions] = useState<any[]>([]);
const [fetchedData, setFetchedData] = useState<any>();
// all
const [selectedOpt, setSelectedOpt] = useState<any>([]);
// API Call for Dropdown Options :
useEffect(() => {
if (!serviceConfig) return;
service[serviceConfig]
.getDataByQuery(query || "")
.then(({ data }: { data: any[] }) => {
let dataSet = processResponse ? processResponse(data) : data;
if (dataSet.data) {
dataSet = dataSet.data;
}
setFetchedData(dataSet);
let tempOptions = dataSet.map((item: any) => {
return {
name: referentialNameKey ? item[referentialNameKey] : item["name"],
value: referentialIdKey ? item[referentialIdKey] : item["id"],
} as never;
});
tempOptions.unshift({ name: "All", value: "all" });
console.log("tempOptions >>>> ", tempOptions);
setAsyncOptions(tempOptions);
})
.catch((err: any) => console.log("Error! : ", err));
}, []);
if (field.value.length > 0 && asyncOptions !== []) {
let fullObjValues = asyncOptions.filter((option) =>
field.value.includes(option.value)
);
console.log("fullObjValues >>> ", fullObjValues);
if (fullObjValues.length > 0) {
setFieldValue(name, fullObjValues);
}
}
const handleChange = (event: any, newValue: any) => {
console.log("AsyncSearchableEvent (value) >>> ", newValue);
setFieldValue(name, newValue);
};
const getOptionLabelCustom = (option: any) => {
if (typeof option != "object") {
let optionTitle: any = asyncOptions.find(
(element: { value: any }) => element?.value == option
);
return optionTitle?.name;
} else {
return option?.name;
}
};
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
const getComponent = () => {
return (
<Autocomplete
// {...field}
disableCloseOnSelect
multiple
options={options ? options : asyncOptions}
value={field.value}
limitTags={2}
getOptionLabel={(option) => getOptionLabelCustom(option)}
onChange={(event, value) => handleChange(event, value)}
renderOption={(option, { selected }) => {
// 'all' option
const selectOptIndex = selectedOpt.findIndex(
(opt: any) => opt.name.toLowerCase() === "all"
);
if (selectOptIndex > -1) {
selected = true;
}
//
return (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.name}
</React.Fragment>
);
}}
renderInput={(params) => (
<TextField
{...params}
label={label}
classes={classes}
fullWidth
variant="outlined"
/>
)}
/>
);
};
return (
<div
className={styles.element_wrapper}
style={{ marginBottom: marginBottom }}
>
<Field
{...field}
id={name}
name={name}
disabled={disabled}
component={getComponent}
autoComplete="off"
/>
<div className={styles.error}>
<ErrorMessage name={name} />
</div>
</div>
);
};
export default AsyncSearchMultiSelect; ```
try to controll it with open :
const [open, setOpen] = React.useState(false);
<Autocomplete
disabled={disabled}
id={name}
name={name}
sx={{width: "100%"}}
open={open}
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}

Spread types may only be created from object type

I have this JavaScript code which I want to use into TypeScript project. But I get these errors. Do you know how I can fix these issues?
const next = () => {
const data = {
currency,
iban,
confirmIban
}
history.push({
pathname: '/summary',
state: { ...location.state, ...data }
})
}
For this line state: { ...location.state, ...data } I get TS2698: Spread types may only be created from object types.
<TextField label="Your test" variant="outlined" onChange={(e) => {
setTest(e.target.value)
}} />
For this line (e) I get TS7006: Parameter 'e' implicitly has an 'any' type.
EDIT: complete code:
import { useState } from 'react';
import { useHistory, useLocation } from 'react-router-dom'
import { Container, FormControl, Typography, Grid, TextField, Button } from '#material-ui/core';
import SelectOption from '../../components/Navbar/Select/SelectOption';
export default function BankDetails() {
const [currency, setCurrency] = useState();
const [iban, setIban] = useState();
const [confirmIban, setConfirmIban] = useState();
const history = useHistory();
const location = useLocation();
const next = () => {
const data = {
currency,
iban,
confirmIban
}
history.push({
pathname: '/summary',
state: { ...location.state, ...data }
})
}
return (
<Container>
<Typography variant="h4">Select an account for payouts</Typography>
<FormControl>
<Grid xs={12}>
<SelectOption handleValue={(e) => {
setCurrency(e.target.value)
}}>
<option value="">Select your currency</option>
<option value="lev">USD</option>
</SelectOption>
</Grid>
<Grid xs={12}>
<label>IBAN</label>
<TextField label="Your number" variant="outlined" onChange={(e) => {
setIban(e.target.value)
}} />
</Grid>
<Grid xs={12}>
<label>Confirm USD</label>
<TextField label="Confirm your USD" variant="outlined" onChange={(e) => {
setConfirmIban(e.target.value)
}} />
</Grid>
<Button variant="contained" color="primary" onClick={next}>
Continue
</Button>
</FormControl>
</Container>
)
}
1. TS2698: Spread types may only be created from object types.
for this one, you should write your push state with an object.
Note: you should know that location.state is undefined for first and you are using ...location.state in the object but you don't know your state is an object or not. so you should write a simple condition for that.
let prevState = {}
if (location.state) {
prevState = { ...location.state as {} }
}
history.push({
pathname: '/summary',
state: { ...prevState, ...data }
})
2. TS7006: Parameter 'e' implicitly has an 'any' type.
for these parameters, you can simply write any type.
<SelectOption handleValue={(e: any) => {
setCurrency(e.target.value)
}}>
Or you can add this rule to your tsconfig.json file:
"noImplicitAny": false
Try this aproach, let me know if works.
export interface Location<S = LocationState> {
pathname: Pathname;
search: Search;
state: S as object;
hash: Hash;
key?: LocationKey | undefined;
}
export interface Location<S = LocationState> {
pathname: Pathname;
search: Search;
state: Object.assign({}, S);
hash: Hash;
key?: LocationKey | undefined;
}
import { FormEvent } from "react";
<TextField
label="Your test"
variant="outlined"
onChange={(e: FormEvent<HTMLInputElement>): => setTest(e.target.value) />

Facing issue while using composition with a parent and a base component for react.js/typescript

What i'm trying to achieve is to have a BaseComponent which will be reuse in different ParentComponent.
My base card component props is ->
export type MSGameCardProps = {
title: string;
fetchGamesFn : (searchText: string) => Promise<IResultObject<any,any>>;
};
My base card render all the necessary basic logics and controls (inputs,autocomplete,title).
For example, it provide an autocomplete which have a simple search debounce functionality.
Me parent component will not necessary have props and will use the base card like so :
export type MSGameSrcCardProps = {};
export const MSGameSrcCard: React.FC<MSGameSrcCardProps> = () => {
const gameSvc = useGameService();
const fetchGame = async (searchText: string) => {
const rs = await result(gameSvc.getAll(searchText));
return rs;
};
return (
<MSGameCard title={"Convert From"} fetchGamesFn={fetchGame}></MSGameCard>
);
};
export default MSGameSrcCard;
The parent component will provide a fetchGames function which can be different.
It will also set the title and may later on set some other flags.
This pattern result with this error : Type '{}' is missing the following properties from type 'MSGameCardProps': title, fetchGamesFn when trying to use the parent component in my page like so : <MSGameSrcCard></MSGameSrcCard>
I don't understand why my parent should have those properties since they are only required in the child component and are fullfill in my parent component function.
I don't want to make them optional(?) since they are actually required; of course only for my base component
I did try to export my basecomponent AS ANY which remove the error but now my props.fetchGamesFn is always undefined even passing it in inside my parent component function.
Maybe i'm doing it wrong but is there a way to have a parent components with no props with child that required props?
EDIT : Here is my MSGameCard base component definition
export const MSGameCard: React.FC<MSGameCardProps> = props => {
const [games, setGames] = React.useState([
{
name: ""
}
]);
const [selectedGame, setSelectedGame] = React.useState<any>();
const [previousGame, setPreviousGame] = React.useState<any>();
const [isLoading, setIsLoading] = React.useState(false);
const [opacity, setOpacity] = React.useState(0);
const fetchGameBase = (searchText: string) => {
setIsLoading(true);
console.log(props.fetchGamesFn);
props.fetchGamesFn(searchText).then(rs =>{
if (rs.isSuccess) setGames(rs.result.data);
setIsLoading(false);
})
};
const searchDebounce = debounce(300, fetchGameBase);
React.useEffect(() => {
fetchGameBase("");
}, []);
const onGameChanged = (event: any, value: any) => {
if (selectedGame) setPreviousGame(selectedGame);
setOpacity(0);
if (value) {
setTimeout(() => {
setSelectedGame(value);
setOpacity(0.2);
}, 300);
}
};
const onInputChanged = (e: any) => {
let value = e.target.value;
if (!value) value = "";
searchDebounce(value);
};
const getSelectedGameImg = () => {
const bgUrl: string = selectedGame
? selectedGame.bg_url
: previousGame?.bg_url;
return bgUrl;
};
return (
<Card style={{ position: "relative", zIndex: 1 }} variant="outlined">
<CardContent style={{ zIndex: 1 }}>
<Typography variant="h5" gutterBottom>
{props.title}
</Typography>
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<Autocomplete
options={games}
getOptionLabel={option => option.name}
onChange={onGameChanged}
onInputChange={onInputChanged}
renderInput={params => (
<TextField
{...params}
label="Source game"
fullWidth
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{isLoading ? (
<CircularProgress color="primary" size={30} />
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
)
}}
/>
)}
/>
</Grid>
</Grid>
<Grid container direction="row" spacing={3}>
<Grid item xs={12} sm={6}>
<TextField label="DPI" fullWidth />
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Sensitivity" fullWidth />
</Grid>
</Grid>
</CardContent>
<img
style={{ opacity: opacity }}
className="gameImage"
src={getSelectedGameImg()}
/>
</Card>
);
};
export default MSGameCard;
Keep updating notice
After checked the minimum reproducible example you have provided.
I found no type error, am I missing something?
Since the error occurred in both two props, I would leave only the string for the check
import * as React from "react";
import "./styles.css";
export type MSGameCardProps = {
title: string;
};
export type MSGameSrcCardProps = {};
export const MSGameSrcCard: React.SFC<MSGameSrcCardProps> = () => {
return <MSGameCard title={"Convert From"} />;
};
const MSGameCard: React.SFC<MSGameCardProps> = (props: MSGameCardProps) => {
console.log(props); // Object {title: "Convert From"}
return <></>;
};
export default function App() {
return (
<div className="App">
<MSGameSrcCard />
</div>
);
}
Try it online here:

Resources