babel-plugin-jsx icon indicating copy to clipboard operation
babel-plugin-jsx copied to clipboard

jsx onClick is not assignable to type of .vue (custom component)

Open icuxika opened this issue 3 years ago • 8 comments

VButton

<script setup lang="ts">
import { computed } from "vue";

interface Props {
	theme?: string;
	size?: string;
	level?: string;
	disabled?: boolean;
	loading?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
	theme: "button",
	size: "normal",
	level: "normal",
	disabled: false,
	loading: false,
});

const classes = computed(() => {
	return {
		[`v3-theme-${props.theme}`]: props.theme,
		[`v3-size-${props.size}`]: props.size,
		[`v3-level-${props.level}`]: props.level,
	};
});
</script>
<template>
	<button class="v3-button" :class="classes" :disabled="props.disabled">
		<span v-if="props.loading" class="v3-loading-indicator"></span>
		<span v-else class="v3-loading-indicator-empty"></span>
		<slot />
	</button>
</template>

use VButton

export const DemoX = defineComponent({
	props: {
		component: {
			type: Object as PropType<Component>,
			required: true,
		},
	},
	setup(props, { attrs, slots, emit, expose }) {
		// 渲染源码
		const html = computed(() => {
			return Prism.highlight(
				(props.component as ComponentType).__sourceCode,
				Prism.languages.html,
				"html"
			);
		});
		// 源码是否可见
		const codeVisible = ref(false);
		const toggleCodeVisible = () =>
			(codeVisible.value = !codeVisible.value);
		return () => (
			<div class={style.container}>
				<h2>{(props.component as ComponentType).__sourceCodeTitle}</h2>
				<div class={style.component}>{h(props.component)}</div>
				<div class={style.action}>
						<VButton onClick={withModifiers(toggleCodeVisible, ["self"])}>
							{codeVisible.value ? "隐藏源代码" : "查看源代码"}
						</VButton>
				</div>
				{codeVisible.value ? (
					<div class={style.code}>
						<pre class={"language-html"} innerHTML={html.value} />
					</div>
				) : (
					<div />
				)}
			</div>
		);
	},
});

Question

It works normally when I use yarn dev, but it will report error when I use vue-tsc --noEmit && vite build

error TS2322: Type '{ onClick: (event: Event, ...args: unknown[]) => any; }' is not assignable to type 'IntrinsicAttributes & Partial<{ loading: boolean; disabled: boolean; size: string; theme: string; level: string; }> & Omit<Readonly<ExtractPropTypes<{ theme: { type: PropType<string>; } & { ...; }; size: { ...; } & { ...; }; level: { ...; } & { ...; }; disabled: { ...; } & { ...; }; loading: { ...; } & { ...; }; }>>...'.
  Property 'onClick' does not exist on type 'IntrinsicAttributes & Partial<{ loading: boolean; disabled: boolean; size: string; theme: string; level: string; }> & Omit<Readonly<ExtractPropTypes<{ theme: { type: PropType<string>; } & { ...; }; size: { ...; } & { ...; }; level: { ...; } & { ...; }; disabled: { ...; } & { ...; }; loading: { ...; } & { ...; }; }>>...'.

56       <VButton onClick={withModifiers(toggleCodeVisible, ["self"])}>
                  ~~~~~~~
error Command failed with exit code 2.

icuxika avatar Apr 21 '22 03:04 icuxika

I temporarily avoided this problem by wrapping a layer of div and setting the click event to the div.

<div onClick={withModifiers(toggleCodeVisible, ["stop"])}>
	<VButton>
		{codeVisible.value ? "隐藏源代码" : "查看源代码"}
	</VButton>
</div>

Should I add an onClick prop to the VButton to solve this problem more rationally?

icuxika avatar Apr 21 '22 03:04 icuxika

@icuxika here is component example. Hope it will help you. If you have any questions, you are free to ask. 🙃

funny-family avatar Apr 21 '22 13:04 funny-family

@icuxika, you did not provide $attrs

<template>
	<button class="v3-button" :class="classes" :disabled="props.disabled" v-bind="$attrs">
		<span v-if="props.loading" class="v3-loading-indicator"></span>
		<span v-else class="v3-loading-indicator-empty"></span>
		<slot />
	</button>
</template>

Also I dont know if .tsx file will catch all component types that come from .vue component.

funny-family avatar May 18 '22 05:05 funny-family

@funny-family Thanks, As a component in the library, I directly used this button by import the .vue and encountered this problem. Today I saw your new answer and try to delete the div, but there are some changes, which I didn't import

icuxika avatar May 18 '22 12:05 icuxika

@icuxika, I still recommend you to take a lot at example of component. And yes, as you mentioned, vue3 and jsx have "some details", but in reality it has "a lot" of details.

Always glad to help 🙂

funny-family avatar May 18 '22 12:05 funny-family

@icuxika Thank you for your advice. I will try to learn everything about example.

icuxika avatar May 18 '22 12:05 icuxika

image Type '{ onClick: () => boolean; }' is not assignable to type 'IntrinsicAttributes & Partial ...

Manually adding HTMLAttributes to merge into Props is a bit burdensome. It requires a lot of boilerplate code to satisfy a basic need.

Using v-bind="$attrs" is not working. I add defineEmits(['click']); and <button @click="$emit('click')" /> as a temporary workaround. (although it is not a good one)

Looking forward to future updates.

mockingjet avatar Jun 09 '23 04:06 mockingjet

@mockingjet I avoided this by using the global registration of the Vue component, but the TypeScript attribute hint for is incorrect in the .tsx file, even though there are no errors, and using in the .vue file, everything works as expected.

https://github.com/icuxika/vue-scaffold-ui/blob/main/src/main.ts

import {
	create as createVUI,
	VBanner,
	VButton,
	VMarkdown,
	VVerification,
	VConfigProvider,
} from "@icuxika/vue-scaffold-ui";

const vueScaffoldUI = createVUI({
	components: [VBanner, VButton, VMarkdown, VVerification, VConfigProvider],
});

https://github.com/icuxika/vue-scaffold-ui/blob/main/lib/create.ts

const create = ({
	components = [],
	componentPrefix = "V",
}: VCreateOption): VInstance => {
	const registerComponent = (
		app: App,
		name: string,
		component: Component
	) => {
		const registered = app.component(componentPrefix + name);
		if (!registered) {
			app.component(componentPrefix + name, component);
		}
	};

	const install = (app: App) => {
		components.forEach((component) => {
			registerComponent(app, component.name ?? "", component);
		});
	};

	return {
		install,
	};
};

https://github.com/icuxika/vue-scaffold-ui/blob/main/lib/button/Button.vue

<template>
	<button class="v3-button" :class="classes" :disabled="props.disabled">
		<span v-if="props.loading" class="v3-loading-indicator"></span>
		<span v-else class="v3-loading-indicator-empty"></span>
		<slot />
	</button>
</template>

https://github.com/icuxika/vue-scaffold-ui/blob/main/src/views/components/DemoX.tsx

return () => (
			<div class={style.container}>
				<h2>{(props.component as ComponentType).__sourceCodeTitle}</h2>
				<div class={style.component}>{h(props.component)}</div>
				<div class={style.action}>
					<v-button
						onClick={withModifiers(toggleCodeVisible, ["stop"])}
					>
						{codeVisible.value ? "隐藏源代码" : "查看源代码"}
					</v-button>
				</div>
				{codeVisible.value ? (
					<div class={style.code}>
						<pre class={"language-html"} innerHTML={html.value} />
					</div>
				) : (
					<div />
				)}
			</div>
		);

icuxika avatar Jun 09 '23 07:06 icuxika