I want to write a TypeScript type based on a union such that each member of the union is transformed a certain way if it matches a certain shape, and is otherwise discarded. In working on this, I found that in TypeScript, the result of applying a type to a generic is different than substituting that type into the body of the generic in place of the type parameter. For example:
type T = "a" | "b" | "c" | 1 | 2 | 3;
// type U = never
type U = T extends string ? T : never;
type GV<GT> = GT extends string ? GT : never;
// type V = "a" | "b" | "c"
type V = GV<T>;
Type U
and V
are different, even though substituting T
into the body of GV
in place of GT
would yield exactly the definition of U
.
Given that referential transparency is not an accurate way to think about TypeScript generics, what is the intended way to think about them? Where is this behavior documented? I couldn’t find any information about it in the official documentation for generics.