Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating a component in a generic function with props of those generic types causes compiler error #61

Open
6XGate opened this issue Jun 7, 2020 · 2 comments

Comments

@6XGate
Copy link

6XGate commented Jun 7, 2020

Creating a component constructor from a generic function that has any properties of those types will not compile in markup. It produces the following compiler error based on the provided example:

${projectDir}/render/app/components/test.tsx:54:36: No overload matches this call.
  Overload 1 of 3, '(options?: ThisTypedComponentOptionsWithArrayProps<{ _tsxattrs: TsxComponentAttrs<{ options: T[]; } & { value?: V | undefined; }, {}, {}>; } & Vue & { innerValue: number | undefined; } & { onChange(value: number): void; } & { ...; } & { ...; }, object, object, object, never> | undefined): CombinedVueInstance<...>', gave the following error.
    Type '{ label: string; value: number; }[]' is not assignable to type 'T[]'.
      Type '{ label: string; value: number; }' is not assignable to type 'T'.
        'T' could be instantiated with an arbitrary type which could be unrelated to '{ label: string; value: number; }'.
  Overload 2 of 3, '(options?: ThisTypedComponentOptionsWithRecordProps<{ _tsxattrs: TsxComponentAttrs<{ options: T[]; } & { value?: V | undefined; }, {}, {}>; } & Vue & { innerValue: number | undefined; } & { onChange(value: number): void; } & { ...; } & { ...; }, object, object, object, object> | undefined): CombinedVueInstance<...>', gave the following error.
    Type '{ label: string; value: number; }[]' is not assignable to type 'T[]'.
  Overload 3 of 3, '(options?: ComponentOptions<{ _tsxattrs: TsxComponentAttrs<{ options: T[]; } & { value?: V | undefined; }, {}, {}>; } & Vue & { innerValue: number | undefined; } & { onChange(value: number): void; } & { ...; } & { ...; }, ... 4 more ..., Record<...>> | undefined): CombinedVueInstance<...>', gave the following error.
    Type '{ label: string; value: number; }[]' is not assignable to type 'T[]'. [TS2769]

Example:

import { PropOptions, PropType, VNode } from "vue";
import * as tsx from "vue-tsx-support";

const options = [
    { label: "Option One", value: 1 },
    { label: "Option Two", value: 2 },
    { label: "Option Three", value: 3 },
];

type Option = typeof options[number];

function select<T, V extends number|string>(predicate: (option: T) => [ string, V ]) {
    return tsx.component({
        name:  "Select",
        props: {
            value:   { default: undefined } as PropOptions<V>,
            options: { type: Array as PropType<T[]>, required: true },
        },
        data: function () {
            return {
                innerValue: this.value as V|undefined,
            };
        },
        computed: {
            label(): string {
                const value = this.options.find(option => predicate(option)[1] === this.innerValue);

                return value ? predicate(value)[0] : "Required";
            },
        },
        methods: {
            onChange(value: V) {
                this.innerValue = value;
                this.$emit("change", value);
            },
        },
        render(): VNode {
            return (<select value={this.innerValue} onChange={value => this.onChange(value as any)}>{
                this.options.map(option => (
                    <option value={predicate(option)[1]}>{predicate(option)[0]}</option>
                ))
            }</select>);
        },
    });
}

// OptionSelect is typeof Select<Option, number>;
const OptionSelect = select((option: Option) => [ option.label, option.value ]);

// @vue/component
const test = tsx.component({
    name: "test",
    render(): VNode {
        return (<div><OptionSelect options={options}/></div>);
    },
});

type test = InstanceType<typeof test>;
export default test;
@wonderful-panda
Copy link
Owner

Yes, that does not work.

As a workaround, you must specify return type explicitly

for vue-tsx-support (V2)

import Vue from "vue";
import * as tsx from "vue-tsx-support";

function select<T, V extends number|string>(predicate: (option: T) => [ string, V ]) {
    return tsx.component({
        /* ... */
    }) as tsx.TsxComponent<Vue, { value?: V, options: T[] }>;
}

for vue-tsx-support@beta (3.0.0-preview.1)

import Vue from "vue";
import * as tsx from "vue-tsx-support";
import { WithProps } from "vue-tsx-support/lib/advance";

function select<T, V extends number|string>(predicate: (option: T) => [ string, V ]) {
    return tsx.component({
        /* ... */
    }) as WithProps<typeof Vue, { value?: V, options: T[] }>;
}

@6XGate
Copy link
Author

6XGate commented Jun 17, 2020

Yes, that can ouch depending on how complex the public interface of the component should be. But that's my current work-around, or just not using generics in the edge cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants