We can easily restrict the supported value using literal types such as below
type GenderConfig = {
gender: 1 | 0
}
const obj: GenderConfig = {
gender: 100 // throw error because value is not 1 or 0
}
Now as much as possible I would like to avoid magic number 1 & 0
so I tried to define an enum for that
enum Gender {
MALE= 1,
FEMALE=0,
}
type GenderConfig = {
gender: Gender
}
const obj: GenderConfig = {
gender: 100 // This doesn't throw error anymore?
}
Surprisingly, gender: 100 is no longer throwing error which I don't understand the rationale behind this?
What you've created here is a numeric enum. The generated JavaScript looks like this:
var Gender;
(function (Gender) {
Gender[Gender["MALE"] = 1] = "MALE";
Gender[Gender["FEMALE"] = 0] = "FEMALE";
})(Gender || (Gender = {}));
In the console, this object looks like this:
Object
0: "FEMALE"
1: "MALE"
FEMALE: 0
MALE: 1
Under the hood, the enum is really just a JavaScript object with properties. It has the named properties you defined, and they are assigned a number representing the position in the enum that they exist (FEMALE being 0, MALE being 1), but the object also has number keys with a string value representing the named constant.
Therefore, you can pass in numbers to a function that expects an enum. The enum itself is both a number and a defined constant.
This appears to defeat the type-safe aspect of TypeScript, since you can pass an arbitrary number to a function expecting this enum. There are two reasons why this is useful, though:
Regarding point (1): if you declare an enum without numeric values, e.g. this:
enum Gender {
MALE = "Male",
FEMALE = "Female"
}
then you can no longer do this:
const gender: Gender = "Male";
You'll have to do an explicit cast:
const gender = "Male" as Gender;
const MALE = 1;
const FEMALE = 0;
type Gender = typeof MALE | typeof FEMALE;
type GenderConfig = {
gender: Gender
}
const obj: GenderConfig = {
gender: 100 // This is now throwing error
}
Instead of using enums, I've changed it to type and by using the combination of const and typeof, I can avoid magic number and improve readability and consistency, thanks others for the explanation
I do not use enums that much anymore because they are kind of complicated. Maybe this is possible somehow but you can use strings like this instead:
type Gender = 'female' | 'male';
type GenderConfig = {
gender: Gender
}
Of course you may need to add some mapping if you really need the number representation at some point.