I have a following enum to handle HTTP statuses:
export enum HttpStatus {
OK = 200,
//other statuses
}
and I have to change this code to use the new enum:
//simplified for demonstration
type GetDataResponse =
{
status: 200;
body: {
data: number;
};
}
| {
status: 100 | 101 | 102 | 201 | 511;
body: unknown;
};
type Data = (GetDataResponse & { status: HttpStatus.OK})["body"]["data"];
200 there is a type, so the goal is to replace 200 with HttpStatus.OK and have the same value (200). However, TypeScript considers it not 200, but HttpStatus.OK, and that results as never in the Data type.
How should I change my code to use enum value?
5
TypeScript enum
s are not meant to be completely interchangeable with the corresponding literal types. That way you don’t accidentally mix enums. If HttpStatus.OK
and SomeOtherEnum.ABC
are both 200
, TypeScript will still complain if you assign SomeOtherEnum.ABC
where an HttpStatus
is expected. That’s because HttpStatus.OK
and SomeOtherEnum.ABC
are considered to be nominal subtypes of 200
, and not assignable to each other.
If you want HttpStatus.OK
and 200
to be completely interchangeable, then you might not want an enum
at all. Indeed, you might perfer a const
-asserted plain object instead:
export const HttpStatus = {
OK: 200,
NOT_FOUND: 400
} as const
This is similar to your enum, but now the types of the values are just numeric literals, not special enum values. There are more differences; see Enum vs As Const for more information. But if you do this your original code works, more or less (except that there’s no type named HttpStatus.OK
, so you need typeof
):
type Data = (GetDataResponse & { status: typeof HttpStatus.OK })["body"]["data"];
// ^? type Data = number
On the other hand if you need enum
s but want to be able to extract the 200
type from HttpStatus.OK
, then you can use conditional type inference on template literal types to tease out that information. It could look like this:
type WidenEnum<T extends string | number> =
T extends number ? `${T}` extends `${infer N extends number}` ? N : T : `${T}`;
export enum HttpStatus {
OK = 200,
NOT_FOUND = 404
}
type W = WidenEnum<HttpStatus.OK>
// ^? type W = 200
enum TestEnum {
A = 1.23,
B = "hello",
}
type AW = WidenEnum<TestEnum.A>
// ^? type AW = 1.23
type BW = WidenEnum<TestEnum.B>
// ^? type BW = "hello"
type TW = WidenEnum<TestEnum>
// ^? type TW = 1.23 | "hello"
And now you can use WidenEnum
in your Data
type:
type Data = (GetDataResponse & { status: WidenEnum<HttpStatus.OK> })["body"]["data"];
// ^? type Data = number
Playground link to code