Explore more macros and syntax sugar to Vue.
—
Core macros for Vue component definition and configuration.
Define component options within <script setup> blocks.
/**
* Define component options (Vue 2.7+ / Vue 3.0-3.2)
* @param options - Component options object
*/
declare function defineOptions<
RawBindings = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
>(
options?: ComponentOptionsWithoutProps<
{},
RawBindings,
D,
C,
M,
Mixin,
Extends
> & {
emits?: undefined;
expose?: undefined;
slots?: undefined;
}
): void;
// Component options type (from Vue)
type ComponentOptionsWithoutProps<
V = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin
> = ComponentOptionsBase<V, D, C, M, Mixin, Extends> & {
props?: undefined;
} & ThisType<CreateComponentPublicInstance<{}, RawBindings, D, C, M, Mixin, Extends>>;Usage Examples:
<script setup>
import { someDirective } from './directives';
import { mixinA, mixinB } from './mixins';
// Define component options
defineOptions({
name: "MyComponent",
inheritAttrs: false,
// Local registrations
components: {
CustomButton: () => import('./CustomButton.vue'),
},
directives: {
focus: someDirective,
},
// Mixins and extension
mixins: [mixinA, mixinB],
extends: BaseComponent,
// Lifecycle hooks
beforeCreate() {
console.log('Component being created');
},
created() {
console.log('Component created');
},
// Additional options
provide() {
return {
theme: 'dark',
};
},
});
// Your composition API code
const count = ref(0);
</script>Define component slots with TypeScript support.
/**
* Define component slots with types (Vue 2.7+ / Vue 3.0-3.2)
* @returns Slots object with type definitions
*/
declare function defineSlots<
S extends Record<string, any> = Record<string, any>
>(): UnwrapSlotsType<S>;
// Slots utility types
type UnwrapSlotsType<S> = {
[K in keyof S]: S[K] extends (...args: any[]) => any ? S[K] : Slot<S[K]>;
};
type Slot<T = any> = (...args: T extends any ? [T] : []) => VNode[];
// Slots type inference
type SlotsType<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends (...args: infer P) => any
? (...args: P) => VNode[]
: Slot<T[K]>;
};Usage Examples:
<script setup>
// Define slots with types
const slots = defineSlots<{
default(): any;
header(props: { title: string; level: number }): any;
footer(props: { year: number }): any;
item(props: { item: any; index: number }): any;
}>();
// Check if slots exist
const hasHeader = !!slots.header;
const hasFooter = !!slots.footer;
// Use in render logic
function renderContent() {
return [
slots.header?.({ title: "My Title", level: 1 }),
slots.default?.(),
slots.footer?.({ year: new Date().getFullYear() }),
];
}
</script>
<template>
<div class="component">
<header v-if="hasHeader">
<slot name="header" :title="title" :level="1" />
</header>
<main>
<slot />
</main>
<footer v-if="hasFooter">
<slot name="footer" :year="2024" />
</footer>
</div>
</template>Define single emit function with enhanced typing.
/**
* Define single emit function with enhanced typing
* @param name - Event name
* @returns Emit function for the specified event
*/
declare function defineEmit<T extends (...args: any[]) => any>(
name: string
): T;
/**
* Define emit with payload type
* @param name - Event name
* @returns Emit function with typed payload
*/
declare function defineEmit<T = any>(
name: string
): (payload: T) => void;
// Emit function types
type EmitFunction<T> = T extends (...args: infer P) => any
? (...args: P) => void
: (payload: T) => void;Usage Examples:
<script setup>
// Define individual emit functions
const emitUpdate = defineEmit<(value: string) => void>("update");
const emitChange = defineEmit<(oldValue: string, newValue: string) => void>("change");
const emitDelete = defineEmit("delete"); // No payload
const emitSelect = defineEmit<{ id: number; name: string }>("select");
// Usage in component logic
function handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
emitUpdate(value);
}
function handleChange(oldVal: string, newVal: string) {
emitChange(oldVal, newVal);
}
function handleDelete() {
emitDelete();
}
function handleSelect(item: { id: number; name: string }) {
emitSelect(item);
}
</script>
<template>
<div>
<input @input="handleInput" @change="handleChange" />
<button @click="handleDelete">Delete</button>
<button @click="handleSelect({ id: 1, name: 'Item' })">Select</button>
</div>
</template>Define render function for component.
/**
* Define render function for component
* @param render - Render function
*/
declare function defineRender(render: RenderFunction): void;
/**
* Define render function with JSX
* @param render - JSX render function
*/
declare function defineRender(render: () => JSX.Element): void;
// Render function types
type RenderFunction = (
props: Record<string, any>,
context: SetupContext
) => VNode | VNode[] | string | number | null | undefined;
interface SetupContext<E = EmitsOptions> {
attrs: Data;
slots: Slots;
emit: EmitFunction<E>;
expose: (exposed?: Record<string, any>) => void;
}Usage Examples:
<script setup>
import { h } from 'vue';
// Props for render function
interface Props {
title: string;
items: Array<{ id: number; name: string }>;
}
const props = defineProps<Props>();
// Define render function with h()
defineRender(() => {
return h('div', { class: 'container' }, [
h('h1', props.title),
h('ul',
props.items.map(item =>
h('li', { key: item.id }, item.name)
)
),
]);
});
// Alternative: JSX render function
// defineRender(() => (
// <div class="container">
// <h1>{props.title}</h1>
// <ul>
// {props.items.map(item => (
// <li key={item.id}>{item.name}</li>
// ))}
// </ul>
// </div>
// ));
</script>Export values for parent component access.
/**
* Export values for parent component access via template refs
* @param exposed - Object with exposed values
*/
declare function exportExpose<T extends Record<string, any>>(
exposed: T
): void;
// Alternative syntax using variable names
declare function exportExpose<T extends Record<string, any>>(): T;Usage Examples:
<script setup>
import { ref, computed } from 'vue';
const count = ref(0);
const message = ref('Hello');
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value++;
}
function reset() {
count.value = 0;
message.value = 'Hello';
}
// Export specific methods and values
exportExpose({
count: readonly(count),
message: readonly(message),
doubleCount,
increment,
reset,
// Can also expose computed or reactive values
state: computed(() => ({
count: count.value,
message: message.value,
})),
});
</script>Supporting types for component definition macros.
// Component option types from Vue
interface ComputedOptions {
[key: string]: ComputedGetter<any> | WritableComputedOptions<any>;
}
interface MethodOptions {
[key: string]: Function;
}
interface ComponentOptionsMixin {
[key: string]: any;
}
// Instance types
type CreateComponentPublicInstance<
P = {},
B = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
PublicProps = P
> = ComponentPublicInstance<P, B, D, C, M, Mixin, Extends, PublicProps>;
// Emit types
type EmitsOptions = ObjectEmitsOptions | string[];
interface ObjectEmitsOptions {
[key: string]:
| null
| ((...args: any[]) => any)
| { (...args: any[]): any; };
}
// VNode types
type VNodeChild =
| VNode
| string
| number
| boolean
| null
| undefined
| VNodeArrayChildren;
type VNodeArrayChildren = Array<VNodeArrayChildren | VNodeChild>;
// Data object type
type Data = Record<string, unknown>;Install with Tessl CLI
npx tessl i tessl/npm-unplugin-vue-macros