Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: typings based on object keys that come from enum

I have an enum. I'd like to use its values to create typings for an object where these values became keys. The following example demonstrates what I'd like to achieve:

export enum MyEnum {
  PROP_1 = "prop 1",
  PROP_2 = "prop 2",
  // ... more props
}

export interface MyInterface {
  property: {
    "prop 1": boolean,
    "prop 2": boolean,
    // ... more props
  }
}

It works fine, but I'd like to avoid repeating all the properties from enum in the interface.

Is it possible to do something like:

export enum MyEnum {
  PROP_1 = "prop 1",
  PROP_2 = "prop 2",
  // ... more props
}

// PSEUDOCODE!
export interface MyInterface {
  property: {
    [values from MyEnum]: boolean
  }
}

UPDATE:

Thanks @jcalz for the solution:

enum MyEnum {
  PROP_1 = "prop 1",
  PROP_2 = "prop 2",
  // ... more props
}

interface PropertyInterface extends Record<MyEnum, boolean> {}

interface MyInterface {
  property: PropertyInterface,
}

It works good, according to what I wrote before, but I've just realized that I forgot to mention that I need all these properties to be optional.

The given solution works if a variable that implements MyInterface is defined as follows:

// VALID!
const myVariable: MyInterface = {
  property: {
    "prop 1": true,
    "prop 2": true,
  },
};

But does not work if I omit some keys as follows:

// INVALID :(
const myVariable: MyInterface = {
  property: {
    "prop 1": true,
  },
};
like image 798
wube Avatar asked Oct 30 '18 19:10

wube


People also ask

Can I use enum as a type in TypeScript?

Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript. Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.

Is enum an object in TypeScript?

Enums or enumerations are a new data type supported in TypeScript. Most object-oriented languages like Java and C# use enums. This is now available in TypeScript too. In simple words, enums allow us to declare a set of named constants i.e. a collection of related values that can be numeric or string values.


1 Answers

Support for this was added in TypeScript 2.9 as part of expanded key support mapped types. Specifically:

A mapped type { [P in K]: XXX } permits any K assignable to string | number | symbol.

That means you can make a type alias to a mapped type with enum values as the key:

enum MyEnum {
  PROP_1 = "prop 1",
  PROP_2 = "prop 2",
  // ... more props
}

type MyEnumValuedKeys = { [K in MyEnum]: boolean; }
// identical to 
// type MyEnumValuedKeys = {
//  "prop 1": boolean;
//  "prop 2": boolean;
//  ... more props
// }

You can make mapped type properties optional by adding a ? after the index-like key:

type OptionalMyEnumValuedKeys = { [K in MyEnum]?: boolean; }
// identical to 
// type MyEnumValuedKeys = {
//  "prop 1"?: boolean;
//  "prop 2"?: boolean;
//  ... more props
// }

So you can use OptionalMyEnumValuedKeys as your PropertyInterface. It is a type alias and not an interface. That usually doesn't matter but in case it does, TypeScript 2.8 introduced the abilitity to extend certain concrete mapped types, so you can get an interface like this:

type OptionalMyEnumValuedKeys = { [K in MyEnum]?: boolean; };
interface PropertyInterface extends OptionalMyEnumValuedKeys {} // add no properties

This could also be accomplished by using the built-in Record and Partial type aliases:

interface PropertyInterface extends Partial<Record<MyEnum, boolean>> {}

Okay, hope that helps. Good luck!

like image 64
jcalz Avatar answered Dec 18 '22 22:12

jcalz