v8.0.0
Enhanced Union String in the Constraint Area
The highlight of this milestone release, version 8, is the enhanced union strings in the constraint area!
Previously, we could apply two types of constraints to parameter types:
-
Single Type Constraint
"/users/:id<string>" // => { id: string }
-
Union Type Constraint with Literal Types
"/users/:id<(a|1|true)>" // => { id: "a" | 1 | true }
However, there were some type patterns that couldn't be achieved with this approach.
For example, you couldn’t create a union of primitive types like string|number
. There may also be situations where you want to handle values like "123"
or "true"
as strings without automatic type conversion.
Unfortunately, this was not possible in v7. If you specified <(string|number)>
, it would generate a union of string literals like "string"|"number"
.
To address this, in v8 we introduced manual type conversion support, allowing conversions to primitive types.
This transition is intuitive, simple, and extremely easy to implement!
The key thing to remember is to add *
before the elements in the union string that need to be converted!
This means that any union string without the *
prefix will be treated as a union of string literals.
Prior to v7
const route_config = {
route_1: {
path: "/:param<(string|number)>"
},
route_2: {
path: "/:param<(a|10|true)>"
}
} as const satisfies RouteConfig;
// ...create link generator
link("route_1", { param: "number" });
// Param type is { param: "string" | "number" }
link("route_2", { param: 10 });
// Param type is { param: "a" | 10 | true }
From v8 onwards
const route_config = {
route_1: {
path: "/:param<(string|number)>" // No automatic type conversions
},
route_2: {
path: "/:param<(*string|*number)>"
},
route_3: {
path: "/:param<(abc|123|boolean)>" // No automatic type conversions
},
route_4: {
path: "/:param<(abc|*123|*boolean)>"
}
} as const satisfies RouteConfig;
// ...create link generator
link("route_1", { param: "number" });
// Param type is { param: "string" | "number" }
link("route_2", { param: 123 });
// Param type is { param: string | number }
link("route_3", { param: "boolean" });
// Param type is { param: "abc" | "123" | "boolean" }
link("route_4", { param: true });
// Param type is { param: "abc" | 123 | boolean }
The only breaking change from v7 is this! Since it only affects type inference and does not change function implementations, you can migrate with confidence.
Other Improvements
- Resolved ambiguities in type inference.
- Clarified internal function names and variable names.
- Updated and revised the documentation for v8.