Using Enums as Restricted Key Types in TypeScript: An In-Depth Analysis

Dec 01, 2025 · Programming · 25 views · 7.8

Keywords: TypeScript | Enums | Key Types

Abstract: This article explores how to use enums as restricted key types for objects in TypeScript. By comparing the compilation behavior, type safety, and mutability control between the `in Enum` and `keyof typeof Enum` approaches, it highlights the advantages of using enum values as keys. Through code examples, the article covers numeric, string, and heterogeneous enums, offering practical recommendations to avoid common pitfalls and achieve stricter type constraints.

In TypeScript, object key types are typically restricted to string or number, but enums enable more precise key constraints. Based on community Q&A data, this article delves into using enums as restricted key types, focusing on the differences between in Enum and keyof typeof Enum.

Basic Methods for Using Enums as Key Types

Since TypeScript's 2018 update, the in Enum syntax allows direct use of enum values as object key types. For example:

enum MyEnum {
    First,
    Second
}

let obj: { [key in MyEnum]: any } = { [MyEnum.First]: 1, [MyEnum.Second]: 2 };

If not all enum values are required, optional properties can be used:

let obj: { [key in MyEnum]?: any } = { [MyEnum.First]: 1 };

Core Differences Between in Enum and keyof typeof Enum

in Enum compiles to enum values, while keyof typeof Enum compiles to enum keys. This leads to significant differences in type safety and behavior.

Type Safety Comparison

With in Enum, key types are strictly limited to enum values:

enum MyEnum { First, Second }
let obj: { [key in MyEnum]?: any } = { [MyEnum.First]: 1 };
obj[0] = 1; // Allowed, as 0 is the value of MyEnum.First
obj[42] = 1; // Error: Type '42' cannot be used to index type '{ 0?: any; 1?: any; }'

The compiled type is { 0?: any; 1?: any; }, permitting only enum values 0 and 1.

In contrast, keyof typeof Enum may allow non-enum values in numeric enums:

let obj: { [key in keyof typeof MyEnum]?: any } = { First: 1 };
obj[2] = 1; // May be allowed due to an implicit [x: number] index signature

The compiled type is { [x: number]: any; readonly First?: any; readonly Second?: any; }, introducing an additional numeric index signature.

Mutability Control

in Enum allows object property modifications by default, with immutability optionally added via readonly:

let obj: { readonly [key in MyEnum]?: any } = { [MyEnum.First]: 1 };
obj[MyEnum.First] = 2; // Error: read-only property

keyof typeof Enum generates read-only properties by default, requiring -readonly to remove restrictions:

let obj: { -readonly [key in keyof typeof MyEnum]?: any } = { First: 1 };
obj.First = 1; // Allowed

Extended Applications with Enum Types

The in Enum method works with various enum types:

enum StringEnum {
    First = "YES",
    Second = "NO"
}
let obj: { [key in StringEnum]?: any } = { [StringEnum.First]: 1 };
obj["YES"] = 0; // Allowed

Heterogeneous enums are also supported:

enum HeterogeneousEnum {
    First = 1,
    Second = "TEXT"
}
let obj: { [key in HeterogeneousEnum]?: any } = { [HeterogeneousEnum.First]: 1 };
obj[1] = 0; // Allowed
obj["TEXT"] = 0; // Allowed

Practical Recommendations and Summary

Based on the analysis, in Enum is recommended because it:

keyof typeof Enum may introduce unexpected numeric index signatures in some scenarios, increasing type uncertainty. By understanding these differences, developers can leverage TypeScript's type system more effectively to build robust and maintainable code.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.