When you declare a variable with a specific type like TEnumImplied
, the compiler only recognizes that specified type. Even if you assign a const
-asserted value to it, the variable's type remains unchanged (unlike variables of union types which can have their type narrowed through assignments).
For instance, if we have:
const y: TEnumImplied = {
type: "enum",
values: ["foo", "bar", "baz"] as const
} as const
The type of y
will simply be TEnumImplied
and not influenced by "foo"
, "bar"
, or "baz"
:
y
//^? const y: TEnumImplied
Hence, EVal<typeof y>
equates to { value: string; }
because
TEnumImplied["values"][number]
is
string
:
type E = EVal<typeof y>
// ^? type E = { value: string; }
If this behavior is not desirable, it's advisable not to specify the type for y
.
Perhaps your intention is to merely verify that the type of y
is assignable to TEnumImplied
without changing its actual type. In such cases, you can utilize the satisfies
operator. Instead of writing const vbl: Type = expr;
, you can use const vbl = expr satisfies Type
. Both approaches will raise an error if expr
isn't of type
Type</code, but while the first method widens <code>vbl
to
Type
, the latter infers the type of
vbl
based on
expr
(with
Type
serving as a
contextual type for inference).
Thus, updating the previous example to:
const y = {
type: "enum",
values: ["foo", "bar", "baz"] as const
} as const satisfies TEnumImplied;
Will give you the expected type for y
:
y
//^? const y: {
// readonly type: "enum";
// readonly values: readonly ["foo", "bar", "baz"];
// }
Consequently, the resulting type for EVal<typeof y
> will also be as anticipated:
type E = EVal<typeof y>
// ^? type E = { value: "foo" | "bar" | "baz"; }
(Playground link to original code)