Currently, there is no way to restrict a type parameter such as T
based on a specific value like value
. Even if value === "A"
evaluates to true, it does not necessarily mean that T
is "A"
. For instance, if value
is of type "A" | "B"
, the compiler infers the union type "A" | "B"
for T
. This results in the next
parameter being of type
ComplexType<"A" | "B">
, allowing calls like:
builder(Math.random() <= 0.999 ? "A" : "B")({ value: "B" }); // no error
which could potentially lead to errors in type checking. Unfortunately, there is no direct way in TypeScript to narrow type parameters within generic function bodies, as highlighted in various GitHub issues like microsoft/TypeScript#27808.
As a workaround, you may resort to using type assertions to explicitly define type constraints:
const builder = <T extends AB>(value: T) => (next: ComplexType<T>) => {
if (value === 'A') {
f(next as ComplexType<A>) // okay
}
}
Alternatively, if you wish to enforce stricter type constraints, you can create complex call signatures to simulate the desired type narrowing:
const builder = <T extends "A" | "B">(
value: T & OneOf<T, ["A", "B"]>
) => (next: ComplexType<T>) => {
if (value === 'A') {
f(next as ComplexType<"A">)
}
}
builder(Math.random() <= 0.999 ? "A" : "B"); // error now
builder2("A") // okay
builder2("B") // okay
While these workarounds can help in certain scenarios, the compiler may still struggle with generic conditional types. Type assertions remain a pragmatic approach until TypeScript introduces better support for narrowing type parameters based on specific values.
Playground link to code