typescriptvue.js

Can i set a default value to an prop that is typed by a generic


I'm very experienced with Vue, but I'm a typescript beginner. I have a simple Checkbox.vue component alongside other props I have two optional props checkedValue and uncheckedValue that are typed by two generics TCheckedValue and TUncheckedValue. I what to have default values for these two props and since by definition a generic can accept any type i should be able to set checkedValue: true and uncheckedValue: false but I get the next error:

Vue: Type true is not assignable to type
InferDefault<LooseRequired<{
     label: string;
     checked: boolean;
     indeterminate: boolean;
     checkedValue?: TCheckedValue | undefined;
     uncheckedValue?: TUncheckedValue | undefined;
     error: boolean; 
}>, TCheckedValue | undefined> | undefined

checkedValue?: InferDefault<LooseRequired<{
     label: string
     checked: boolean
     indeterminate: boolean
     checkedValue?: TCheckedValue
     uncheckedValue?: TUncheckedValue
     error: boolean 
}>, TCheckedValue | undefined> | undefined

This is my component code:

<template>
    <div>
        <input
            type="checkbox"
            :id="props.id"
            :class="[
                'rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm',
                'focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800 mr-2'
            ]"
            :value="props.checkedValue"
            :checked="isChecked"
            :indeterminate="props.indeterminate"
            v-bind="$attrs"
            @change="update"
        />
        <InputLabel :inline="true" :for="props.id" :value="props.label" />
    </div>
</template>

<script setup lang="ts" generic="TCheckedValue, TUncheckedValue">
import { type InputPropsType } from "@/Composeables/useFormInput";
import { computed } from "vue";
import InputLabel from "@/Components/Form/InputLabel.vue";

const props = withDefaults(defineProps<InputPropsType & {
    label: string;
    checked: boolean;
    indeterminate: boolean;
    checkedValue?: TCheckedValue;
    uncheckedValue?: TUncheckedValue;
    error: boolean;
}>(), {
    checked: false,
    indeterminate: false,
    checkedValue: true,
    uncheckedValue: false,
    error: false,
});

const emit = defineEmits<{
    (event: 'change', value: TCheckedValue | TUncheckedValue): void
}>();

const inputValue = defineModel<TCheckedValue | TUncheckedValue>();

const update = (e: Event) => {
    const target = e.target as HTMLInputElement;
    inputValue.value = target.checked ? props.checkedValue : props.uncheckedValue;
    emit('change', inputValue.value);
};

const isChecked = computed(() =>
    inputValue.value === props.checkedValue || props.checked
);
</script>

I've spent a week with chatGPT and claude AI trying to figure it out and no joy.

What i tried so far:

<script setup lang="ts" generic="TCheckedValue extends boolean, TUncheckedValue extends boolean">

<script setup lang="ts" generic="TCheckedValue extends boolean = boolean, TUncheckedValue extends boolean = boolean">

<script setup lang="ts" generic="TCheckedValue = boolean, TUncheckedValue = boolean">

// i've also tried to use unknown and any instead of boolean 

const props = withDefaults(defineProps<InputPropsType & {
    // ...
    checkedValue?: TCheckedValue;
    uncheckedValue?: TUncheckedValue;
}>(), {
    // ...
    checkedValue: true as unknown as TCheckedValue,
    uncheckedValue: false as unknown as TUncheckedValue,
});

Solution

  • You could add the default values' types to the props:

        checkedValue?: TCheckedValue | true;
        uncheckedValue?: TUncheckedValue | false;
    

    That gives (property) checkedValue: true | NotUndefined<TCheckedValue> which seems right.

    You could also use the new way of destructuring props:

    const {checkedValue = true, uncheckedValue = false, ...props} = defineProps<{
        label?: string;
        checkedValue?: TCheckedValue;
        uncheckedValue?: TUncheckedValue;
    }>();
    

    That gives checkedValue: true | TCheckedValue which also seems right.