0
votes

I'm trying to write a generic function where the generic type is used to type its parameter.

Here's a part of this parameter :

type Configuration<T> = {
    masterdata: T[],
    target: ????
}

I'm having trouble typing the target property. I would like it to be the name of any property in a specific class (MtmFormComponent, the current class) but with the constraint that the type of the property is T[].

My goal is to write : this[configuration.target] = configuration.masterdata;.

I'm halfway there. Here's how I've typed target for now:

type MtmFormComponentPropertyOfType<T, K extends keyof MtmFormComponent> = MtmFormComponent[K] extends T[] ? K : never;

type DropdownAutocompleteConfiguration<T, K extends keyof MtmFormComponent> = {
    masterdata: T[],

    targetFilteredList: MtmFormComponentPropertyOfType<T, K>,
};

When declaring an object of type DropdownAutocompleteConfiguration, all is good and the auto-complete of my IDE correctly constrains me into using only a key which leads to the same type as the value of masterdata. So my type seems to be correctly defined.

My problem is when using that object in my generic function:

private setupDropdownAutocompleteBehaviour<T, K extends keyof MtmFormComponent>(configuration: DropdownAutocompleteConfiguration<T, K>): void {
        this[configuration.targetFilteredList] = configuration.masterdata;

        // ...
    }

Here, the this[configuration.targetFilteredList] expression gives the following error:

Type 'T[]' is not assignable to type 'this[Currency[] extends T[] ? "filteredCurrencyList" : never] & this[PriceUnit[] extends T[] ? "filteredPriceUnits" : never] & this[Subscription[] extends T[] ? "subscriptions" : never]'.
Type 'T[]' is not assignable to type 'this[Currency[] extends T[] ? "filteredCurrencyList" : never]'.
Type 'T[]' is not assignable to type 'Currency[]'.
Type 'T' is not assignable to type 'Currency'.

From what I understand, inside the function, TypeScript fully resolves the type of this[configuration.targetFilteredList] instead of relying of the fact that it's T[]. From the auto-complete feature of my IDE, I'm sure that I cannot have a value for target which would lead to an type incompatible with the one of masterdata.

I'm not sure what to do from there :/

Thanks for your help :)

1

1 Answers

0
votes

In this particular case, do you have to make your functions generic over the target property key?

To give a concrete example, suppose MtmFormComponent was defined like this

interface MtmFormComponent {
    foo: string[];
    bar: string[];
    baz: number[];
    qux: string;
}

Now, we want a type that gives us all the property names of a type T, where the property name maps to a value that is a U[]. We can define this type like so:

type ArrayValuedProps<T, U> = { [K in keyof T]: (T[K] extends U[] ? K : never) }[keyof T]

This way, we get

ArrayValuedProps<MtmFormComponent, string> = { foo: "foo"; bar: "bar"; baz: never; qux: never }[keyof MtmFormComponent]
                                           = { foo: "foo"; bar: "bar"; baz: never; qux: never }["foo" | "bar" | "baz" | "qux"]
                                           = "foo" | "bar" | never
                                           = "foo" | "bar"

Similarly, ArrayValuedProps<MtmFormComponent, number> is equivalent to the type "baz";

Using this, we can define your Configuration<T> type like so:

type Configuration<T> = {
    masterdata: T[],
    target: ArrayValuedProps<MtmFormComponent, T>
}

and we can completely avoid putting in the target key as a type parameter.