Generic initializer for Record in typescript
Asked Answered
S

1

7

I'm trying to write a function that initialize Record<X,Y>in typescript

my issue is that I'm having the error

X only refers to a type but is use as a value here.

In my case X will always be an enum but I don't know how to precise it in the signature also

Here is the function I have written :

function initiateRecord<X extends string | number,Y>(enumX: typeof X, defaultValue: Y): Record<X,Y>{
    const toReturn = {} as Record<X,Y>;
    Object.keys(enumX).forEach(key => {
         toReturn[key] = defaultValue;
    });
    return toReturn;
}

Complete code to test :

enum EnumA {
    A = 'A',
    B = 'B',
    C = 'C',
}

function initiateRecord<X extends string | number,Y>(enumX: typeof X, defaultValue: Y): Record<X,Y>{
    const toReturn = {} as Record<X,Y>;
    Object.keys(enumX).forEach(key => {
        toReturn[key] = defaultValue;
    });
    return toReturn;
}

class AA {
    bb: Record<EnumA, boolean> = initiateRecord(EnumA, false);

    constructor(){
    }
}

const test = new AA();

test.bb[EnumA.A] = true;
alert(JSON.stringify(test));

Note that when I click on run it seems to work, but that error is bugging me, and i can't add it to my projct like that.

Sternforemost answered 24/9, 2019 at 7:41 Comment(2)
your url doesnt workBaumgartner
Url was too long, ive edited to put the whole testing codeSternforemost
B
7

A few of your types are a little wrong. The type of an enum is more closely typed as {} or {[index: string]: any}

Your return type will be a little different and will in fact be Record<string,Y>

enum EnumA {
    A = 'A',
    B = 'B',
    C = 'C',
}

function initiateRecord<Y>(enumX: {[index: string]: any}, defaultValue: Y): Record<string,Y>{
    const toReturn:Record<string,Y> = {} ;
    Object.keys(enumX).forEach(key => {
        toReturn[key] = defaultValue;
    });
    return toReturn;
}

class AA {
    bb: Record<EnumA, boolean> = initiateRecord(EnumA, false);
    constructor(){
    }
}

const test = new AA();

test.bb[EnumA.A] = true;
alert(JSON.stringify(test));

An even terser (1 liner) syntax can be realised with Object.assign, i.e


function initiateRecord<Y>(enumX: {[index: string]: any}, defaultValue: Y): Record<string,Y>{
  return Object.assign({},...Object.keys(enumX).map(x=>({[x]:defaultValue})))
}

I would however question your approach - what merit your enum values have if they will be immediately overridden?

Baumgartner answered 24/9, 2019 at 7:52 Comment(3)
Can you explain me how to call it ? Because i cannot do bb: Record<EnumA, boolean> = initiateRecord(EnumA, false)Sternforemost
@Sternforemost did you see my updated answer? Do you require further help?Baumgartner
Didn't see the update until you commented sorry, that's what I needed thatnks :)Sternforemost

© 2022 - 2024 — McMap. All rights reserved.