TypeScript failing on React typing - javascript

I'm sure there's something stupid that I'm missing, but I can't get my TypeScript compiler to compile a fairly simply React component. I'm using a .tsx file extension for my file which TypeScript should be able to handle via my webpack config. I get the following error: ERROR in ./src/App.tsx
(36,16): error TS2339: Property 'todos' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<ToDosList> & Readonly<{ children?: ReactNode; }> &...'.
Below is my code. I've tried both implicitly setting the state for the component (meaning without setting state: App.State) and explicitly giving state a type of App.State, but neither seem to work.
import * as React from 'react';
import ToDosList from './components/todos_list';
const todos = [
{
task: 'Make React tutorial',
is_completed: false
},
{
task: 'Eat Dinner',
is_completed: true
}
];
namespace App {
export interface Props {
}
export interface State {
todos: any
}
}
export default class App extends React.Component<App.Props, App.State> {
constructor(props) {
super(props);
this.state = { todos: todos };
}
render() {
return (
<div>
<h1>React ToDos App</h1>
<ToDosList todos={this.state.todos} />
</div>
);
}
}

Related

Convert connect-redux-shorthand notation to JS/TypeScript

While I searched & read around, I am still a newbie (or to be exact, a rusty, part-time JS developer) and my question may be awfully simple.
I am trying to convert an existing JS project to TypeScript. I am struggeling to understand the combination of connect-redux-decorator, JS shorthand notation and arrow nomenclature in one single line:
#connect(({session: { loggedIn }}) => ({loggedIn}), {establishConnection})
I would be very grateful, if someone could translate it to an "export default connect" approach or just a functions/classes notation.
In the following code the line results in this TypeScript error message:
[ts]
Unable to resolve signature of class decorator when called as an expression.
Type 'ComponentClass<Pick<{}, never>, any> & { WrappedComponent: ComponentType<{}>; }' is
not assignable to type 'typeof AuthenticatedApp'.
Type 'Component<Pick<{}, never>, any, any>' is not assignable to type 'AuthenticatedApp'.
Property 'componentDidMount' is optional in type
'Component<Pick<{}, never>, any, any>' but required in type 'AuthenticatedApp'.`
The code:
import * as React from 'react';
import { connect } from 'react-redux'
import Connecting from '../components/Connecting'
import { establishConnection } from '../actions/login'
#connect(({session: { loggedIn }}) => ({loggedIn}), {establishConnection})
export default class AuthenticatedApp extends React.Component {
componentDidMount() {
this.props.establishConnection()
}
componentWillReceiveProps(nextProps) {
if(!nextProps.loggedIn) {
this.props.establishConnection()
}
}
render() {
const { loggedIn, children } = this.props
const appContent = loggedIn ? children : <Connecting />
return (
<div>
{appContent}
</div>
)
}
}
While the error message itself may be also solved totally differently I wanted to replace the decorator by "plain" code, hoping that it solves the issue.
My try looked liked something like this (totally wrong, I know):
function mapStateToProps(loggedIn) {
return {loggedIn} = session: { loggedIn };
}
...
class AuthenticatedApp extends React.Component {
...
export default connect(mapStateToProps, establishConnection)(AuthenticatedApp);
I am using
Visual Studio Code (1.26.1)
"#types/react": "^16.4.13"
"#types/react-dom": "^16.0.7"
"typescript": "^3.0.3"
Thank you!
HerrB92
As a starting point, you would take the exact same connect call expression and apply it to the component manually instead of using a decorator:
export default connect(({session: { loggedIn }}) => ({loggedIn}), {establishConnection})(AuthenticatedApp);
(And remove export default from the class.) Now you'll get a (not very clear) error because TypeScript can't infer the parameter type of the mapStateToProps function. You can annotate it like this:
export default connect(({session: { loggedIn }}: {session: {loggedIn: boolean}}) => ({loggedIn}), {establishConnection})(AuthenticatedApp);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(Maybe you have a type already defined in your codebase that contains {session: {loggedIn: boolean}} or even more.)
Alternatively, you can pull out the mapStateToProps function, leaving it as an arrow function:
const mapStateToProps =
({session: { loggedIn }}: {session: {loggedIn: boolean}}) => ({loggedIn});
export default connect(mapStateToProps, {establishConnection})(AuthenticatedApp);
or convert it to a traditional function definition:
function mapStateToProps({session: { loggedIn }}: {session: {loggedIn: boolean}}) {
return {loggedIn};
}
export default connect(mapStateToProps, {establishConnection})(AuthenticatedApp);
Ultimately, to compile the component successfully, you'll need to specify its props type, something like this:
class AuthenticatedApp extends React.Component<{establishConnection: () => void, loggedIn: boolean}> { ... }
but that's a separate issue from the connect.

Next Js & Typescript - Property setState does not exist on type Example

Code works fine, but I can't figure out how to remove this error in VSCode.
Thanks for help.
import * as React from 'react';
interface State {
text: string;
}
export default class Example extends React.Component<State> {
state: State = {
text: 'SOME TEXT'
}
private handleChange = () => {
this.setState({text: 'New Text'}); //error: property setState does not exist on type Example
}
public render(){
return(
<div>
<h2 onClick={this.handleChange}>{this.state.text}</h2>
</div>
)
}
}
First off, make sure you have the react type definitions installed:
npm install #types/react #types/react-dom
Secondly, the generic for state goes second, not first. The first one is for props.
export default class Example extends React.Component<{}, State> {
Look at the React type definitions to verify this (go to definition on Component). <P, S> means props, then state.
class Component<P, S> {
I resolved the same issue in VSCode just by changing the version of typescript used
=> open command palette > "select typescript version" > "use workspace version"

ForwardRef error with typescript and react-native

I'm getting a ts error when using forwardRef
// [ts] Property 'forwardRef' does not exist on type 'typeof React'.
const MyComponent = React.forwardRef((props: Props, ref: any) => ...
In React Native the parent component is throwing this error:
Invariant Violation: Element type is invalid: expected a string (for build-in components) or a class/function (for composite components) but got: object
Any idea on how to solve it?
According to the definitions:
function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ComponentType<P & ClassAttributes<T>>;
interface RefForwardingComponent<T, P = {}> {
(props: P & { children?: ReactNode }, ref?: Ref<T>): ReactElement<any> | null;
propTypes?: ValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
ref is an optional argument, try the following:
Inside a class create a ref object with a type parameter equal to your desired target (in my case div but View also works in react-native)
private divRef: React.RefObject<div> = React.createRef();
In the interface that represents the props for the forwarded component expose it as an optional property
interface Props {
ref?: React.RefObject<div>;
}
Declare the forwarded component with the type React.ComponentType
const ComponentWithForwardedRef: React.ComponentType<Props> =
React.forwardRef((props: Props, ref?: React.Ref<div>) => (
<div ref={ref}>{props.message}</div>
));
When an instance of the component with the forwarded ref is created send the created ref object as a prop
<ComponentWithForwardedRef ref={this.divRef} />
All in one:
import * as React from "react";
import { render } from "react-dom";
interface Props {
message: string;
ref?: React.RefObject<div>;
}
const ComponentWithForwardedRef: React.ComponentType<Props> =
React.forwardRef((props: Props, ref?: React.Ref<div>) => (
<div ref={ref}>{props.message}</div>
));
class App extends React.Component<Props> {
private divRef: React.RefObject<div> = React.createRef();
public componentDidMount() {
const div = this.divRef.current;
// check the console!
console.log(div);
}
public render() {
return (
<ComponentWithForwardedRef ref={this.divRef} {...this.props} />
)
}
}
render(<App message="hello world" />, document.getElementById("root"));
Link for posterity: https://codesandbox.io/s/6v152q394k
Dependencies (reference purposes)
"#types/react": "^16.3.11",
"#types/react-native": "^0.55.19",
"react-native": "0.55.2",
"typescript": "^2.8.1"

React : Cannot read property 'object' of undefined at Object.defineProperty.value [duplicate]

Using propTypes to validate props gives the following error:
TypeError: Cannot read property 'string' of undefined.
TypeError: Cannot read property 'func' of undefined.
The code in question is at the bottom of the snippet:
import React from 'react';
import ProjectItem from './ProjectItem';
class Projects extends React.Component {
deleteProject(title) {
this.props.onDelete(title);
}
render() {
let projectItems;
if (this.props.project) {
projectItems = this.props.project.map(project => {
return (
<ProjectItem key={project.title} project={project} onDelete={this.deleteProject.bind(this)} />
)
});
}
return (
<div className="Projects">
{projectItems}
</div>
);
}
}
Projects.propTypes = {
projects: React.PropTypes.string,
onDelete: React.PropTypes.func
}
You need to install the prop-types package and then add the import statement
import PropTypes from prop-types;
at the top of your class.
The PropTypes have been moved from React to their own package prop-types.
EDIT: As mentioned in the comments, this is only applicable for React version 15.5 and above.
As palsrealm mentioned, you need to add the prop-types package, and then remove React before Proptypes. The following should work:
Projects.propTypes = {
projects: PropTypes.string,
onDelete: PropTypes.func
}

ReactJS export const and component from one module

I have two modules that I want to share a const array. One of these modules includes both the const array and a component, whilst the other module only includes a component.
This is what I have in module "A".
export const ORDER_COLUMNS = [
{ name: 'orderNumber', title: 'Order', width: '10%', type: 'string' },
{ name: 'orderType', title: 'Type', width: '10%', type: 'string' }
];
class OrderGridControl extends React.Component {
constructor(props) {
super(props);
this.state = {
orderColumns: ORDER_COLUMNS
};
}
...
}
export default OrderGridControl;
And in module "B".
import {OrderGridControl, ORDER_COLUMNS} from 'component/order-grid-control';
class OrderQueryPage extends React.Component {
constructor(props) {
super(props);
this.state = {
orderColumns: ORDER_COLUMNS
};
console.info(this.state.orderColumns);
}
...
render() {
return (
<div>
<PropertyGrid gridSetup={this.state.orderColumns} />
</div>
);
}
}
When I run this I get the following error. invariant.js:39 Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of 'moduleB'.
However, the console.info(this.state.orderColumns) line logs all the column objects I expect.
Interestingly, if I copy the array into module "B" and assign the columns in the constructor exactly the same way it seems to work. It only seems to be an issue when I'm importing from the other module.
You've got it almost right-- you're exporting a default export (OrderGridControl) and a named export (ORDER_COLUMNS).
However, in B.js, you're trying to import two named exports.
Modify your import to look like this:
import OrderGridControl, { ORDER_COLUMNS } from 'component/order-grid-control';
The advantage of having a default export is that you don't have to match its name exactly when importing it, so you could do something like
import GridControl, { ORDER_COLUMNS } from 'component/order-grid-control';

Categories