Allow key-rebinding in a type, with React Component

22 Views Asked by At

I want to be able to have a component in React, that takes an input that has to have 2 mandatory keys, id and name. It's absolutely needed so that the utility function can work properly, since it will be using those fields to access the data of interest.

That, I successfully did !

Now, I want to have an argument keysMapping that allows for my user to be able to remap those needed keys ! So, for instance, if my user has both id and displayName instead, he'd be able to remap the used key as id and displayName. Basically, id and name are used by default.

My current entire system in a MCVE looks like this

// Defining a set of mandatory keys, with their values;
type MandatoryKeys = {
    id: string | number;
    name: string;
};

type KeysMapping<T> = Record<keyof MandatoryKeys, keyof T>;

const useKey = <T extends MandatoryKeys>(
    item: T,
    key: keyof MandatoryKeys,
    keysMapping?: KeysMapping<T>,
) => {
    const keyToUse = keysMapping ? keysMapping[key] : key;
    return item[keyToUse];
}

type MyComponentProps<T> = {
    items: T[],
    keysMapping?: KeysMapping<T>,
}
const MyComponent = <T extends MandatoryKeys,>(props: MyComponentProps<T>) => {
    const { items, keysMapping } = props;

    items.map( (item) => {
        const a = useKey(item, "id", keysMapping);
        const b = useKey(item, "name", keysMapping);
    })

    return <div/>;
}

And for my current use-cases. I feel like I've tried to do hundreds of different combinations, but this is the point where I think I am the closest to succeeding. Though I don't know what to do with this extends MandatoryKeys...

const ParentExample = () => {
    type CT = { id: string, name: string, field: string }
    const data: CT[] = [{ id: "a", name: "b", field: "C" }]
    const mapping: KeysMapping<CT> = { "id": "field", "name": "name" }
    const _1 = <MyComponent items={data} keysMapping={mapping}/>;
    // No error everything is fine !
    const _3 = <MyComponent items={data}/>
    // No error, everything is fine !
    
    
    type CT2 = { name: string, field: string }
    const data2: CT2[] = [{ name: "b", field: "C" }]
    const mapping2: KeysMapping<CT2> = { "id": "field", "name": "name" }
    const _2 = <MyComponent items={data2} keysMapping={mapping2}/>
    // This fails, but I want it to SUCCESS because even though `id` is missing
    // The mapping is here, and should be able to say... It's enough ! I've made the proper checks now I'm here.

    // items :      Property 'id' is missing in type 'CT2' but required in type 'MandatoryKeys'.
    // keysMapping: Type 'KeysMapping<CT2>' is not assignable to type 'KeysMapping<MandatoryKeys>'.
    //              Property 'field' is missing in type 'MandatoryKeys' but required in type 'CT2'.


    const _2 = <MyComponent items/>
    // This fails, but it's normal. I don't have the required keys, AND no mapping is given to balance that.

    return <Fragment />
}

I am also open to new strategies about how to tackle my problem. I felt like the keysMapping was a beautiful way of handling that bit of code, but maybe I'm entirely going the wrong way...

0

There are 0 best solutions below