1.3.4 • Published 3 years ago
vue-tsx-macros v1.3.4
vue-tsx-macros
Description
Set of marcos for convenient definitions of Vue JSX components.
Enhances developer experience by removing duplication and improving type inference.
Write type-safe components with less extra tooling (like vue-tsc/Volar).
Includes
typescripttypesbabelplugin for code transformation
Features
component$- infers component
namefrom function or variable name - infers component
propssimilar todefineProps()andwithDefaults()fromvue - infers component
emitssimilar todefineEmits()fromvue
- infers component
useRender$- allows to expose instance properties with correct type inference and at the same time use
renderfunction insidesetup
- allows to expose instance properties with correct type inference and at the same time use
defaultProps$- adds type assistance for default props definition, in case you want to reuse them outside of
withDefaults()
- adds type assistance for default props definition, in case you want to reuse them outside of
How to use
- Install
npm install vue-tsx-macros- Include babel plugin into your build configuration
- Vite
Add babel plugin to @vitejs/plugin-vue-jsx options
import vueJsx from "@vitejs/plugin-vue-jsx";
export default {
plugins: [
vueJsx({
babelPlugins: ["vue-tsx-macros/babel-plugin"],
}),
],
};If you are using nuxt with vite - use extendViteConfig.
- Babel
Add plugin to your babel config alongside @vue/babel-plugin-jsx
{
"plugins": ["@vue/babel-plugin-jsx", "vue-tsx-macros/babel-plugin"]
}Plugin options
{
/**
* If false, usage of emits argument in `component$` will throw compile-time error
* @default true
*/
allowEmits: boolean;
}- Use
import { ref } from "vue";
import { component$, useRender$ } from "vue-tsx-macros";
export const Example = component$().define((props, { emit }) => {
const counterRef = ref<InstanceType<typeof Counter>>();
onMounted(() => {
const id = setInterval(() => {
counterRef.value?.increment();
}, 5000);
onUnmounted(() => clearInterval(id));
});
return () => (
<div>
{"A counter"}
<Counter ref={counterRef} />
</div>
);
});
type CounterProps = {
initial?: number;
};
type CounterEmits = {
update: (value: number) => void;
};
const Counter = component$<ExampleProps, ExampleEmits>().define(
(props, { emit }) => {
const count = ref(props.initial || 0);
const increment = (value = 1) => {
count.value += value;
emit("update", counter.value);
};
useRender$(() => (
<button onClick={() => increment()}>{count.value}</button>
));
return { increment };
}
);
Counter.inheritAttrs = false;Transform examples
- Standard component
export const Example = component$<{ initialValue?: number }>().define(
(props) => {
const counter = ref(props.initialValue || 0);
const onClick = () => (counter.value += 1);
return () => <button onClick={onClick}>{counter.value}</button>;
}
);export const Example = {
name: "Example",
props: ["initialValue"],
setup: (props) => {
const counter = ref(props.initialValue || 0);
const onClick = () => (counter.value += 1);
return () => <button onClick={onClick}>{counter.value}</button>;
},
};- With defaultProps
export const Example = component$<{ initialValue?: number }>()
.withDefaults({ initialValue: 0 })
.define((props) => {
const counter = ref(props.initialValue);
const onClick = () => (counter.value += 1);
return () => <button onClick={onClick}>{counter.value}</button>;
});const _defaultProps = {
initialValue: 0,
};
export const Example = defineComponent({
name: "Example",
props: {
initialValue: {
type: null,
default: _defaultProps["initialValue"],
},
},
setup: (props) => {
const counter = ref(props.initialValue);
const onClick = () => counter.value + 1;
return () => <button onClick={onClick}>{counter.value}</button>;
},
});- With emits
type Props = {
initialValue: number;
};
type Emits = {
update: (value: number) => void;
};
export const Example = component$<Props, Emits>().define((props, { emit }) => {
const counter = ref(props.initialValue);
const onClick = () => {
counter.value += 1;
emit("update", counter.value);
};
return () => <button onClick={onClick}>{counter.value}</button>;
});type Props = {
initialValue: number,
};
type Emits = {
update: (value: number) => void,
};
export const Example = defineComponent({
name: "Example",
props: ["initialValue"],
emits: ["update"],
setup: (props, { emit }) => {
const counter = ref(props.initialValue);
const onClick = () => {
counter.value += 1;
emit("update", counter.value);
};
return () => <button onClick={onClick}>{counter.value}</button>;
},
});- Expose properties
export const Example = component$().define(() => {
const counter = ref(0);
const increment = () => (counter.value += 1);
useRender$(() => <button onClick={increment}>{counter.value}</button>);
return { increment };
});let _render;
export const Example = defineComponent({
name: "Example",
setup: () => {
const counter = ref(0);
const increment = () => (counter.value += 1);
_render = () => <button onClick={increment}>{counter.value}</button>;
return {
increment,
};
},
render() {
return _render();
},
});- Functional component
export const Example = component$<{ size?: number }>().functional(
({ size = 500 }) => {
return <div data-size={size} />;
}
);export const Example = Object.assign(
({ size = 500 }) => {
return <div data-size={size} />;
},
{
displayName: "Example",
props: ["size"],
}
);Limitations
- In
component$, type parameters forpropsandemitsmust beTypeLiteralorTypeReferencetoInterfaceDeclarationorTypeAliasDeclarationwithTypeLiteralin the same file (similar todefineProps()fromvue). Note that only explicitly enumerated properties are included.
// ok
export const Example = component$<{ size: number }>().define(...)
// ok
export interface ExampleProps {
size: number
}
export const Example = component$<Props>().define(...)
// ok
export type ExampleProps = {
size: number
}
export const Example = component$<Props>().define(...)
// compile-time error
import { SomeProps } from './some-file'
export const Example = component$<SomeProps>().define(...)
// compile-time error
export type ExampleProps = { size: number } & { extra: number }
export const Example = component$<ExampleProps>().define(...)useRender$must be used only incomponent$().define(function.
// ok
export const Example = component$().define(() => {
const counter = ref(0);
const increment = () => (counter.value += 1);
useRender$(() => <button onClick={increment}>{counter.value}</button>);
return { increment };
});
// compile-time error
export const Example = component$().define((props) => {
const counter = ref(0);
const increment = () => {
counter.value += 1;
useRender$(() => <button onClick={increment}>{counter.value}</button>);
};
return { increment };
});