Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue3 TypeScript props with multiple types

As an exercise, I'm trying to define the FontAwesome Vue implementation in TypeScript. They allow the icon prop to have different types:

icon: {
    type: [Object, Array, String],
    required: true
},

I tried to add a validation to it, but then props in setup seems broken:

validator: (prop) => {
    if (typeof prop === 'object') {
        const obj = prop as any;
        return (obj.prefix && obj.iconName);
    } else if (Array.isArray(prop) && prop.length === 2) {
        return true;
    }
    return typeof prop === 'string';
}
Property 'icon' does not exist on type 'Readonly<{ [x: number]: string; } & { length?: number | undefined; toString?: string | undefined; toLocaleString?: string | undefined; concat?: string[] | undefined; join?: string | undefined; slice?: string[] | undefined; ... 16 more ...; flat?: unknown[] | undefined; }> | Readonly<...>'.
Property 'icon' does not exist on type 'Readonly<{ [x: number]: string; } & { length?: number | undefined; toString?: string | undefined; toLocaleString?: string | undefined; concat?: string[] | undefined; join?: string | undefined; slice?: string[] | undefined; ... 16 more ...; flat?: unknown[] | undefined; }>'.Vetur(2339)

Without the validator, I can do this in the setup:

setup(props) {
    let icon: Icon; // simple interface I defined having prefix and iconName
    if (typeof props.icon === 'object') {
        icon = props.icon as Icon;
    } else if (Array.isArray(props.icon) && props.icon.length === 2) {
        icon = {
            prefix: props.icon[0],
            iconName: props.icon[1],
        };
    } else if (typeof props.icon === 'string') {
        icon = {
            prefix: 'l',
            iconName: props.icon as string,
        };
    }
}

Any ideas on how I could make the validation work with this setup? or is there a better way to define a prop with multiple types?

like image 556
Thomas Avatar asked Oct 09 '20 11:10

Thomas


1 Answers

From how I understand the docs something like the following would be needed:

import { defineComponent, PropType } from 'vue'

interface Icon {
  src: string
  width: number
  height: number
}

const Component = defineComponent({
  props: {
    icon: {
        type: [Object, Array, String] as PropType<Icon | Icon[] | string>,
        required: true
    },
  }
})
like image 64
Anbraten Avatar answered Nov 15 '22 12:11

Anbraten