JavaScript values cannot be directly created from TypeScript types due to the fact that the static type system gets erased when TypeScript is compiled to JavaScript. Therefore, defining type Foo
and type Bar
will not allow you to create const Foo
and const Bar
. The workaround is to first create values and then use them to assist in creating types.
Creating the Foo
type is relatively straightforward:
const Foo = ["Foo", "Foo2", "Foo3"] as const;
type Foo = typeof Foo[number];
The const
assertion enables the compiler to recognize the literal types of the elements within Foo
, allowing you to derive the union of those element types by indexing into Foo
using a number
key.
However, constructing the Bar
type is more complicated. Here's a basic method to accomplish it:
const Bar = ["Bar", "Bar2", ...Foo.map(t => `${t}Type` as const)] as const;
console.log(Bar);
// const Bar: readonly ["Bar", "Bar2", ...("FooType" | "Foo2Type" | "Foo3Type")]
type Bar = typeof Bar[number];
// type Bar = "Bar" | "Bar2" | "FooType" | "Foo2Type" | "Foo3Type"
In this approach, the map()
array method is utilized to add "Type"
to every element of Foo
.
It's important to note that the callback function is defined as t => `${t}Type` as const
with its own const
assertion. This instructs the compiler to determine what the template literal expression `${t}Type` evaluates to as a template literal type. Since t
is inferred as type
Foo</code, <code>`${t}Type` as const
is inferred as type
`${Foo}Type`
, aligning with your intention.
While the TypeScript call signature for the map()
method returns an unordered array type rather than a tuple type like that of
Foo</code, ordering isn't relevant in this scenario. Thus, the conclusion suffices for the task at hand.</p>
<p>Upon evaluation of the line <code>const Bar = ["Bar", "Bar2", ...Foo.map(✂) as const] as const
, another
const
assertion is made, ensuring that the compiler recognizes
"Bar"
and
"Bar2"
. As a result, the type of the
Bar
value is determined as:
// const Bar: readonly ["Bar", "Bar2", ...("FooType" | "Foo2Type" | "Foo3Type")[]]
When querying the compiler for the element type by indexing into typeof Bar
using a number
, the desired outcome is achieved:
type Bar = typeof Bar[number];
// type Bar = "Bar" | "Bar2" | "FooType" | "Foo2Type" | "Foo3Type"
This establishes the intended structure of the Bar
type.
Explore code on Playground