Reputation: 771
I like the type-safety bit of using a Record in Typescript but seem to be in a bind with respect to looping through keys enums and populating the record
export enum Key {
A = 'A',
B = 'B',
C = 'C'
}
export interface Value {
isAvailable: boolean;
reasons: string[];
}
export type Access = Record<Key, Value>;
export function access() {
// I would like to avoid this initialization but TS does not allow it because it defeats the
// purpose and makes me initialize a value for each key in the enum upfront.
const featureAccess: Access = {
[Key.A]: null,
[Key.B]: null,
[Key.C]: null,
};
Object.keys(Key).forEach((eachKey: string) => {
const feature = Access[eachKey];
featureAccess[feature] = {
isAvailable: ..., // Populate from API
reasons: ...// Populate from API
};
});
return featureAccess;
}
Is this a wrong candidate for using the Typescript Record?
Upvotes: 2
Views: 2856
Reputation: 2676
My answer comes very late after the OP asked the question.
I was facing the same issue and I must admit the @Johannes Brodwall answer is very cool.
But after refexion, I figured out maybe my need was not so relevant : semantically, a Record is a bunch of properties defining one (and only) object. Here, we are trying to create group of X similar objects, with an index to access each of them quickly. It looks like we need a Map, not a Record. :D
So I suggest you to modify your code to use a Map instead of a Record. In addition to solving this init problem, I am sure it will better match your global needs all around your application.
export enum Key {
A = 'a',
B = 'b',
C = 'c'
}
export interface Value {
isAvailable: boolean;
reasons: string[];
}
// Need to define a class to use the 'new Access()' further.
class Access extends Map<Key, Value > {}
export function access(): Access {
const featureAccess: Access = new Access();
Object.keys(Key).forEach((eachKey: string) => {
featureAccess.set(eachKey as Key, {
isAvailable: ..., // Populate from API
reasons: ...// Populate from API
});
});
return featureAccess;
}
And further, you can easily do :
const obj: Access = access();
obj.has(Key.A);
obj.get(Key.A);
obj.keys();
obj.values();
obj.foreach(...);
obj.delete();
obj.size;
.....
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Upvotes: 0
Reputation: 7821
You can at least make it work by going through an any
type.
export function access() {
const featureAccess: any = {};
Object.keys(Key).forEach((eachKey: string) => {
const feature = Access[eachKey];
featureAccess[feature] = {
isAvailable: ..., // Populate from API
reasons: ...// Populate from API
};
});
return featureAccess as Access;
}
This is the only way I've found to avoid the Record type to be optional (i.e. Record<Foo, Bar|undefined>
). It feels like a hack to go through any
so I would love an improvement.
Upvotes: 1
Reputation: 518
this question may have been quite a while, but in case there are people who want to know the answer, we can use Partial for the Record.
enum Key {
A = 'a',
B = 'b',
C = 'c'
}
interface Value {
isAvailable: boolean;
reasons: string[];
}
type Access = Partial<Record<Key, Value>>;
then we can iterate the enum and assign it to the Record variable.
const featureAccess: Access = {};
Object.keys(Key).forEach((enumKey) => {
console.log(enumKey);
featureAccess[enumKey] = ..... // assign the value
});
or we can use the enum values as well
const featureAccess: Access = {};
Object.values(Key).forEach((enumValue) => {
console.log(enumValue);
featureAccess[enumValue] = ..... // assign the value
});
Upvotes: 5