# Perspective ComponentMeta Typing Issue

**URL:** <https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730>\
**Category:** Module Development\
**Tags:** perspective\
**Created:** [August 15, 2024, 11:24pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730 "2024-08-15T23:24:52Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [August 15, 2024, 11:24pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/1 "2024-08-15T23:24:52Z")

</div>

I feel like I am missing the smallest thing here, however for some reason when I am trying to build this very simple button component, it is giving me an error about the `Component` class not being assignable to type `PComponent` (Both things inside the `@inductiveautomation/perspective-client` library that I have not messed with in my install.

Am I missing something here?

## src/components/button.tsx

```python
/**
 * Example of a component which displays an Button, given a URL.
 */
import * as React from 'react';
import {
	Component,
	ComponentMeta,
	ComponentProps,
	PComponent,
	PropertyTree,
	SizeObject
} from '@inductiveautomation/perspective-client';

export const COMPONENT_TYPE = "example.input.button";

export interface ButtonProps {
	text: string;   
}

export class Button extends Component<ComponentProps<ButtonProps>, any> {
	render() {
		const { props: { text }, emit } = this.props;
		return (
			<div { ...emit() }>
				{text}
			</div>
		);
	}
}

export class ButtonMeta implements ComponentMeta {

	getComponentType(): string {
		return COMPONENT_TYPE;
	}

	getViewComponent(): PComponent {
		return Button;
	}

	getDefaultSize(): SizeObject {
		return ({
			width: 360,
			height: 360
		});
	}

	getPropsReducer(tree: PropertyTree): ButtonProps {
		return {
			text: tree.readString("text", "")
		};
	}
}

```

### package.json

```python
{
	"name": "@me/dumb-button",
	"version": "0.0.1",
	"description": "A stupid button that won't compile",
	"main": "dist/index.js",
	"types": "dist/index.d.ts",
	"scripts": {
		"build": "webpack --mode production"
	},
	"dependencies": {
		"@inductiveautomation/perspective-client": "2.1.34",
		"axios": "^0.28.0",
		"react": "16.14.0",
		"react-dom": "16.14.0"
	},
	"devDependencies": {
		"@types/react": "16.8.8",
		"@types/react-dom": "16.8.3",
		"css-loader": "^6.1.2",
		"mini-css-extract-plugin": "^2.6.0",
		"ts-loader": "^9.2.9",
		"typescript": "4.2.3",
		"webpack": "^5.93.0",
		"webpack-cli": "4.9.2"
	}
}

```

### tsconfig.json

```python
{
	"compilerOptions": {
		"allowJs": false,
		"allowSyntheticDefaultImports": true,
		"baseUrl": ".",
		"paths": {
			"*": [
				"*"
			]
		},
		"jsx": "react",
		"module": "commonjs",
		"noEmit": false,
		"outDir": "dist/",
		"pretty": true,
		"sourceMap": true,
		"strict": true,
		"target": "es6",
		"lib": [
			"es6",
			"dom",
			"es7",
			"es2017"
		],
		"forceConsistentCasingInFileNames": true,
		"types": [
			"react",
			"react-dom"
		]
	},
	"include": [
		"./src/**/*.ts",
		"./src/**/*.tsx"
	],
	"exclude": [
		"node_modules",
		"dist"
	]
}

```

### The error itself:

```python
ERROR in /Users/kgamble/Work/Ignition/modules/My Modules/dumb-button/web/src/components/Button.tsx
./src/components/Button.tsx 53:2-16
[tsl] ERROR in /Users/kgamble/Work/Ignition/modules/My Modules/dumb-button/web/src/components/Button.tsx(53,3)
      TS2322: Type 'typeof Button' is not assignable to type 'PComponent'.
  Type 'typeof Button' is not assignable to type 'ComponentClass<ComponentProps<PlainObject, PlainObject>, any>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'ComponentProps<PlainObject, PlainObject>' is not assignable to type 'Readonly<ComponentProps<ButtonProps, PlainObject>>'.
        Types of property 'props' are incompatible.
          Property 'text' is missing in type 'PlainObject' but required in type 'ButtonProps'.

```

I appreciate any advice!

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [August 16, 2024, 3:19pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/2 "2024-08-16T15:19:13Z")

</div>

I have found that I have to cast my component `as PComponent` in `getViewComponent` to make Typescript happy, this issue might be the same?

```js
getViewComponent(): PComponent {
        return ChartjsComponent as PComponent
    }

```

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [August 16, 2024, 3:25pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/3 "2024-08-16T15:25:52Z")

</div>

> [@bmusson](#):
>
> ` as PComponent`

Unfortunately I tried this as well and no luck

```js
getViewComponent(): PComponent {
	return Button as PComponent;
}

```

### Error:

```python
Conversion of type 'typeof Button' to type 'PComponent' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type 'typeof Button' is not comparable to type 'ComponentClass<ComponentProps<PlainObject, PlainObject>, any>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'ComponentProps<PlainObject, PlainObject>' is not comparable to type 'Readonly<ComponentProps<ButtonProps, PlainObject>>'.
        Types of property 'props' are incompatible.
          Property 'text' is missing in type 'PlainObject' but required in type 'ButtonProps'.ts(2352)

```

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [August 16, 2024, 3:58pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/4 "2024-08-16T15:58:24Z")

</div>

Hm, I’m not sure. Maybe someone experienced with class based components can chime in, I’ve only used the functional style.

Not sure if it’s useful but this is my “starter” component.

```js

import * as React from 'react'
import {
    ComponentMeta,
    ComponentProps,
    PComponent,
    PropertyTree,
    SizeObject,
} from '@inductiveautomation/perspective-client'
import { useState } from 'react'

export const COMPONENT_TYPE = 'mussonindustrial.display.example'

interface ExampleComponentProps {
    text?: string
    value?: number
}

export function ExampleComponent(props: ComponentProps<ExampleComponentProps>) {
    const [counter, setCounter] = useState(props.props.value)
    const handleClick = () => {
        if (counter != undefined) {
            setCounter(counter + 1)
        } else {
            setCounter(0)
        }
        props.store.props.write('value', counter)
    }

    return (
        <div {...props.emit({ classes: ['examplecomponent'] })}>
            <span>This is a simple component, with only a "text" prop: </span>
            <span>{props.props.text}</span>
            <span>{props.props.value}</span>
            <button onClick={handleClick}>Increment</button>
        </div>
    )
}

export class ExampleComponentMeta implements ComponentMeta {
    getComponentType(): string {
        return COMPONENT_TYPE
    }

    getDefaultSize(): SizeObject {
        return {
            width: 160,
            height: 64,
        }
    }

    getPropsReducer(tree: PropertyTree): ExampleComponentProps {
        return {
            text: tree.readString('text', 'Default Text!'),
            value: tree.readNumber('value', 0),
        }
    }

    getViewComponent(): PComponent {
        return ExampleComponent
    }
}

```

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [August 16, 2024, 4:31pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/5 "2024-08-16T16:31:44Z")

</div>

Weirdly even if I switch to a function I am getting the same error...

### src/components/button.tsx

```ts
/**
 * Example of a component which displays an Button, given a URL.
 */
import * as React from 'react';
import {
	Component,
	ComponentMeta,
	ComponentProps,
	PComponent,
	PropertyTree,
	SizeObject
} from '@inductiveautomation/perspective-client';
import { useState } from 'react'

export const COMPONENT_TYPE = "example.input.button";

export interface ButtonProps {
	text: string;   
}

export function ButtonComponent(props: ComponentProps<ButtonProps>) {
    const [text] = useState(props.props.text)
	
	return (
		<div { ...props.emit() }>
			{text}
		</div>
	);
}

export class ButtonMeta implements ComponentMeta {

	getComponentType(): string {
		return COMPONENT_TYPE;
	}

	getViewComponent(): PComponent {
		return ButtonComponent;
	}

	getDefaultSize(): SizeObject {
		return ({
			width: 360,
			height: 360
		});
	}

	getPropsReducer(tree: PropertyTree): ButtonProps {
		return {
			text: tree.readString("text", "")
		};
	}
}

```

### error

```python
Type '(props: ComponentProps<ButtonProps, PlainObject>) => Element' is not assignable to type 'PComponent'.
  Type '(props: ComponentProps<ButtonProps, PlainObject>) => Element' is not assignable to type 'FunctionComponent<ComponentProps<PlainObject, PlainObject>>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'PropsWithChildren<ComponentProps<PlainObject, PlainObject>>' is not assignable to type 'ComponentProps<ButtonProps, PlainObject>'.
        Types of property 'props' are incompatible.
          Property 'text' is missing in type 'PlainObject' but required in type 'ButtonProps'.ts(2322)

```

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [August 16, 2024, 5:05pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/6 "2024-08-16T17:05:52Z")

</div>

I figured it out! (I think)

I needed to make my component props optional?

```ts
export interface ButtonProps {
	text?: string;   
}

```

I am not really sure why I needed to do this.

Also, @bmusson out of curiosity, why use the functional style over class based? any reason or just happens to be what you did?

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [August 16, 2024, 5:27pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/7 "2024-08-16T17:27:56Z")

</div>

> [@kgamble](#):
>
> Also, @bmusson out of curiosity, why use the functional style over class based? any reason or just happens to be what you did?

Functional components seem to be the newest paradigm for React, and since I had no previous experience it made sense to only focus on the current best-practice (I don’t want to be a web developer, learning React is just a means to an end 😆).

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [August 16, 2024, 7:23pm UTC](https://forum.inductiveautomation.com/t/perspective-componentmeta-typing-issue/94730/8 "2024-08-16T19:23:25Z")

</div>

Your functional example builds on my machine, with the addition of

```diff
getViewComponent(): PComponent {
+	return ButtonComponent as PComponent;
}

```

~~I can't convince Typescript to be happy with the class-based version at all 🤷‍♂️~~

```diff
getViewComponent(): PComponent {
+	return Button as any;
}

```

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/6/c6797ce15594a9d9645cb760034f3027c1ee863b.jpeg)
