# TypeScript Usage Guide

TypeScript patterns and best practices for Grimoire v3.

---

## 📦 Type Imports

### Component Prop Types

All components export their props as TypeScript interfaces:

```typescript
import type { GButtonProps, GInputProps, GDropdownProps } from '@twentyfourg/grimoire';

// Use in your own components
interface MyComponentProps extends GButtonProps {
  customProp?: string;
}
```

### Component Instance Types (Exposed Types)

v3 exports specific `*Exposed` types for all components that use `defineExpose`, giving you full TypeScript autocomplete for component methods:

```typescript
import { ref } from 'vue';
import type {
  GDropdownExposed,
  GInputExposed,
  GFormExposed
} from '@twentyfourg/grimoire';

// ✅ BEST - Use specific exposed types
const dropdownRef = ref<GDropdownExposed>();
const inputRef = ref<GInputExposed>();
const formRef = ref<GFormExposed>();

// TypeScript now knows ALL methods with full autocomplete!
dropdownRef.value?.open();      // ✅ VueMultiselect method
dropdownRef.value?.toggle();    // ✅ Custom GDropdown method
inputRef.value?.focus();        // ✅ Custom GInput method
formRef.value?.validateForm();  // ✅ GForm method
```

**Available Exposed Types:**

| Component | Exposed Type | Includes |
|-----------|-------------|----------|
| `GDropdown` | `GDropdownExposed` | All VueMultiselect methods + custom props (toggle, isDropdownOpen) |
| `GDatepicker` | `GDatepickerExposed` | All VueDatePicker methods + custom props |
| `GInput` | `GInputExposed` | focus, blur, clear, select, resize methods |
| `GForm` | `GFormExposed` | validateForm, submit, clearForm, resetFormValidation |
| `GFormField` | `GFormFieldExposed` | updateValue, validation state |
| `GFormFieldInput` | `GFormFieldInputExposed` | validate, updateValue, resetValidation |
| `GModal` | `GModalExposed` | open, close, toggle methods |
| `GTable` | `GTableExposed` | changeSorting, table state |
| `GTabs` | `GTabsExposed` | activateTab, tab navigation |
| `GIcon` | `GIconExposed` | Icon element reference |
| `GAvatar` | `GAvatarExposed` | Image loading state |
| `GThumbnail` | `GThumbnailExposed` | Image loading state |
| `GCollapsible` | `GCollapsibleExposed` | toggleOpen, toggleClose, toggle |
| `GPagination` | `GPaginationExposed` | prevPage, nextPage, page selection |

**Fallback (for components without exposed types):**

```typescript
import type { ComponentPublicInstance } from 'vue';

// Generic approach (less type-safe)
const buttonRef = ref<ComponentPublicInstance | null>(null);
```

---

## 🎯 Using Grimoire with TypeScript

### Basic Component Usage

```vue
<template>
  <GButton @click="handleClick">Click Me</GButton>
  <GInput v-model="text" placeholder="Enter text" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GButton, GInput } from '@twentyfourg/grimoire';

const text = ref<string>('');

function handleClick(): void {
  console.log('Clicked!');
}
</script>
```

### Typed Refs

```vue
<template>
  <GDropdown ref="dropdownRef" v-model="selected" :options="options" />
  <GButton @click="openDropdown">Open</GButton>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import type { GDropdownExposed } from '@twentyfourg/grimoire';
import { GDropdown, GButton } from '@twentyfourg/grimoire';

// ✅ Use specific exposed type for full TypeScript support
const dropdownRef = ref<GDropdownExposed>();
const selected = ref<string | null>(null);
const options = ref<string[]>(['Option 1', 'Option 2', 'Option 3']);

function openDropdown(): void {
  dropdownRef.value?.open(); // ✅ TypeScript autocomplete works!
}

onMounted(() => {
  // TypeScript knows ALL available methods with full autocomplete
  dropdownRef.value?.open();      // VueMultiselect method
  dropdownRef.value?.close();     // VueMultiselect method
  dropdownRef.value?.clear();     // VueMultiselect method
  dropdownRef.value?.toggle();    // Custom GDropdown method

  // Access custom properties
  console.log(dropdownRef.value?.isDropdownOpen); // Custom property
});
</script>
```

---

## 🎨 Creating Typed StyledComponents

### Basic StyledComponent with TypeScript

```vue
<!-- StyledButton.vue -->
<template>
  <GButton ref="buttonRef" v-bind="mergedProps" class="styled-button">
    <slot />
  </GButton>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GButton } from '@twentyfourg/grimoire';
import type { GButtonProps } from '@twentyfourg/grimoire';
import { useStyledComponent } from '@twentyfourg/grimoire/composables';

// Extend base props with custom variants
interface StyledButtonProps extends GButtonProps {
  variant?: 'default' | 'primary' | 'danger' | 'success';
}

const props = defineProps<StyledButtonProps>();

const buttonRef = ref<InstanceType<typeof GButton> | null>(null);

const { mergedProps, expose } = useStyledComponent(buttonRef, props, {
  defaults: {
    rounded: true,
  },
});

defineExpose(expose);
</script>
```

### StyledComponent with Custom Methods

```vue
<!-- StyledDropdown.vue -->
<template>
  <GDropdown ref="dropdownRef" v-bind="mergedProps" class="styled-dropdown">
    <template v-for="(_, name) in $slots" v-slot:[name]="slotData">
      <slot :name="name" v-bind="slotData" />
    </template>
  </GDropdown>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';
import { GDropdown } from '@twentyfourg/grimoire';
import type { GDropdownProps } from '@twentyfourg/grimoire';
import { useStyledComponent } from '@twentyfourg/grimoire/composables';

interface StyledDropdownProps extends GDropdownProps {
  variant?: 'default' | 'outlined' | 'lined';
}

const props = defineProps<StyledDropdownProps>();

const dropdownRef = ref<InstanceType<typeof GDropdown> | null>(null);

const isOpen = computed(() => dropdownRef.value?.isOpen ?? false);

const { mergedProps, expose } = useStyledComponent(dropdownRef, props, {
  defaults: {
    canClear: false,
    canDeselect: false,
  },
  computeProps(merged, attrs) {
    const mode = attrs.mode || merged.mode;

    // Auto-configure multi-select modes
    if (mode === 'tags' || mode === 'multiple') {
      merged.hideSelected = merged.hideSelected ?? false;
      merged.closeOnSelect = merged.closeOnSelect ?? false;
    }

    return merged;
  },
  expose: {
    // Add custom methods
    clearAndFocus(): void {
      dropdownRef.value?.clear();
      dropdownRef.value?.activate();
    },
    isOpen,
  },
});

defineExpose(expose);
</script>
```

---

## 🔧 Advanced TypeScript Patterns

### Typed Form Data

```vue
<template>
  <GForm @submit="handleSubmit">
    <GInput v-model="form.name" label="Name" required />
    <GInput v-model="form.email" type="email" label="Email" required />
    <GInput v-model.number="form.age" type="number" label="Age" />
    <GButton type="submit">Submit</GButton>
  </GForm>
</template>

<script setup lang="ts">
import { reactive } from 'vue';
import { GForm, GInput, GButton } from '@twentyfourg/grimoire';

interface UserForm {
  name: string;
  email: string;
  age: number | null;
}

const form = reactive<UserForm>({
  name: '',
  email: '',
  age: null,
});

function handleSubmit(): void {
  // form is fully typed
  console.log('Name:', form.name);
  console.log('Email:', form.email);
  console.log('Age:', form.age);
}
</script>
```

### Typed Dropdown Options

```vue
<template>
  <GDropdown v-model="selectedUser" :options="users" label="name" value-prop="id" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GDropdown } from '@twentyfourg/grimoire';

interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
}

const selectedUser = ref<number | null>(null);

const users = ref<User[]>([
  { id: 1, name: 'John Doe', email: 'john@example.com', role: 'admin' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'user' },
]);
</script>
```

### Typed Table Columns

```vue
<template>
  <GTable :columns="columns" :data="users" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GTable } from '@twentyfourg/grimoire';

interface User {
  id: number;
  name: string;
  email: string;
  role: string;
}

interface TableColumn {
  key: keyof User;
  label: string;
  sortable?: boolean;
}

const columns = ref<TableColumn[]>([
  { key: 'name', label: 'Name', sortable: true },
  { key: 'email', label: 'Email', sortable: true },
  { key: 'role', label: 'Role' },
]);

const users = ref<User[]>([
  { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
]);
</script>
```

---

## 🎯 Composable Type Safety

### useStyledComponent with TypeScript

```typescript
import { ref } from 'vue';
import type { Ref } from 'vue';
import { GButton } from '@twentyfourg/grimoire';
import { useStyledComponent } from '@twentyfourg/grimoire/composables';

const buttonRef: Ref<InstanceType<typeof GButton> | null> = ref(null);

const { mergedProps, expose } = useStyledComponent(buttonRef, props, {
  defaults: {
    rounded: true,
  },
});

// expose is fully typed with all GButton methods + custom ones
defineExpose(expose);
```

### Custom Expose Types

```typescript
import type { ComponentPublicInstance } from 'vue';
import { GDropdown } from '@twentyfourg/grimoire';

interface CustomDropdownExpose {
  // Base methods (from GDropdown)
  open: () => void;
  close: () => void;
  clear: () => void;

  // Custom methods
  clearAndFocus: () => void;
  isOpen: boolean;
}

// Later in template:
const dropdownRef = ref<CustomDropdownExpose | null>(null);
```

---

## 🛠️ Utility Types

### Extracting Props from Components

```typescript
import type { ExtractPropTypes } from 'vue';
import { GButton } from '@twentyfourg/grimoire';

// Get prop types from component
type ButtonProps = ExtractPropTypes<typeof GButton.props>;

// Use in your interfaces
interface MyComponentProps extends Partial<ButtonProps> {
  customProp: string;
}
```

### Union Types for Variants

```typescript
// Strict typing for variants
type ButtonVariant = 'default' | 'primary' | 'secondary' | 'danger';
type ButtonSize = 'sm' | 'md' | 'lg';

interface StyledButtonProps extends GButtonProps {
  variant?: ButtonVariant;
  size?: ButtonSize;
}

const props = withDefaults(defineProps<StyledButtonProps>(), {
  variant: 'default',
  size: 'md',
});
```

---

## 🔍 Type Guards

### Checking Component Types

```typescript
import type { Component } from 'vue';
import { GButton, GInput } from '@twentyfourg/grimoire';

function isButtonComponent(component: Component): component is typeof GButton {
  return component === GButton;
}

function isInputComponent(component: Component): component is typeof GInput {
  return component === GInput;
}
```

---

## 📝 Event Typing

### Typed Event Handlers

```vue
<template>
  <GInput v-model="text" @update:modelValue="handleInput" @cleared="handleCleared" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GInput } from '@twentyfourg/grimoire';

const text = ref<string>('');

function handleInput(value: string): void {
  console.log('Input changed:', value);
}

function handleCleared(): void {
  console.log('Input cleared');
}
</script>
```

### Custom Event Types

```typescript
// Define your events
interface FormEvents {
  submit: (data: FormData) => void;
  error: (error: Error) => void;
  'field-change': (field: string, value: unknown) => void;
}

// Use with defineEmits
const emit = defineEmits<FormEvents>();

// Emit with type safety
emit('submit', formData);
emit('error', new Error('Validation failed'));
emit('field-change', 'email', 'test@example.com');
```

---

## 🎨 CSS Variable Typing

### Typed Style Objects

```vue
<template>
  <GButton :style="buttonStyles">Styled Button</GButton>
</template>

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

interface ButtonStyleVars extends CSSProperties {
  '--g-button-bg'?: string;
  '--g-button-color'?: string;
  '--g-button-border-radius'?: string;
}

const buttonStyles = computed<ButtonStyleVars>(() => ({
  '--g-button-bg': '#007bff',
  '--g-button-color': '#ffffff',
  '--g-button-border-radius': '8px',
}));
</script>
```

---

## 🧩 Generic Components

### Creating Generic Typed Components

```vue
<script setup lang="ts" generic="T">
import { ref } from 'vue';
import { GDropdown } from '@twentyfourg/grimoire';

interface Props {
  items: T[];
  modelValue: T | null;
  labelKey: keyof T;
  valueKey: keyof T;
}

const props = defineProps<Props>();

const emit = defineEmits<{
  'update:modelValue': [value: T | null];
}>();

const selected = ref<T | null>(props.modelValue);
</script>
```

---

## ⚠️ Common TypeScript Pitfalls

### Pitfall 1: Forgetting Null Checks

```typescript
// ❌ Wrong - may error if ref is null
const inputRef = ref<InstanceType<typeof GInput> | null>(null);
inputRef.value.focus(); // Error!

// ✅ Correct - use optional chaining
inputRef.value?.focus();
```

### Pitfall 2: Incorrect Ref Types

```typescript
// ❌ Wrong - too generic
const buttonRef = ref<any>(null);

// ✅ Correct - properly typed
const buttonRef = ref<InstanceType<typeof GButton> | null>(null);
```

### Pitfall 3: Missing Type Imports

```typescript
// ❌ Wrong - imports value, not type
import { GButtonProps } from '@twentyfourg/grimoire';

// ✅ Correct - imports type
import type { GButtonProps } from '@twentyfourg/grimoire';
```

---

## 🎓 Best Practices

### 1. Always Use Type Imports

```typescript
// Prefer type imports for interfaces
import type { GButtonProps, GInputProps } from '@twentyfourg/grimoire';

// Regular imports for components
import { GButton, GInput } from '@twentyfourg/grimoire';
```

### 2. Use Strict Null Checks

Enable strict mode in `tsconfig.json`:

```json
{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": true
  }
}
```

### 3. Leverage Type Inference

```typescript
// Let TypeScript infer when possible
const text = ref(''); // inferred as Ref<string>

// Be explicit when needed
const items = ref<User[]>([]); // explicit for empty arrays
```

### 4. Document Custom Types

```typescript
/**
 * User object from the API
 */
interface User {
  /** Unique user ID */
  id: number;
  /** Full user name */
  name: string;
  /** User email address */
  email: string;
}
```

---

## 📚 Related Documentation

- **[Component Usage](./component-usage.md)** - Component examples
- **[Styling Guide](./styling-guide.md)** - CSS and theming
- **[Migration Guide](./v2-to-v3-migration-guide.md)** - Upgrading from v2

---

## 🔗 External Resources

- [Vue 3 TypeScript Guide](https://vuejs.org/guide/typescript/overview.html)
- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html)
- [Vue Type Helpers](https://vuejs.org/api/utility-types.html)

---

**For detailed type definitions, see component source files in [`src/components/`](../../src/components/).**
