Bug Report
π Search Terms
Type aliases, composition, semantics
π Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about type aliases
β― Playground Link
Playground link with relevant code
π» Code
type F<x> = x extends (() => infer o) ? o : never
type G<x> = <o extends x>() => o
type V = string
// const result1 = f(g(v))
type Result1 = F<G<V>> // => string
// const h = x => f(g(x))
type H<x> = F<G<x>>
// const result2 = h(v)
type Result2 = H<V> // => unknown
π Actual behavior
Result1 = string, and Result2 = unknown
π Expected behavior
Result1 = Result2 = string
I have developed (perhaps incorrectly) a mental model of type aliases as functions from the big un(i)kinded collection of all TypeScript types to itself. I don't have any very precise mathematical picture of this collection, but it is something like the quotient of the collection of type-forming expressions by the relation X ~ Y, where X ~ Y iff every expression of type X is assignable to Y and every expression of type Y is assignable to X. So just as e.g. x == y => f(x) == f(y) for a function f and values x, y of its domain, I expect that X ~ Y => F<X> ~ F<Y>.
I understand that there are portals like any through which eldritch horrors can enter the type system and destroy our sanity, but I take it that these are the exception rather than the rule, and that the design of the language tends towards eliminating as many of these as is feasible without significant compromises.
Today I was surprised to discover that in general the type expression F<G<V>> is not equivalent to the expression H<V>, where H is the composition of F and G (i.e. type H<x> = F<G<x>>). Is this a desirable invariant? Is this simply an inaccurate mental model of what type aliases represent?
Bug Report
π Search Terms
Type aliases, composition, semantics
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
Result1 = string, andResult2 = unknownπ Expected behavior
Result1 = Result2 = stringI have developed (perhaps incorrectly) a mental model of type aliases as functions from the big un(i)kinded collection of all TypeScript types to itself. I don't have any very precise mathematical picture of this collection, but it is something like the quotient of the collection of type-forming expressions by the relation
X ~ Y, whereX ~ Yiff every expression of typeXis assignable toYand every expression of typeYis assignable toX. So just as e.g.x == y => f(x) == f(y)for a functionfand valuesx,yof its domain, I expect thatX ~ Y => F<X> ~ F<Y>.I understand that there are portals like
anythrough which eldritch horrors can enter the type system and destroy our sanity, but I take it that these are the exception rather than the rule, and that the design of the language tends towards eliminating as many of these as is feasible without significant compromises.Today I was surprised to discover that in general the type expression
F<G<V>>is not equivalent to the expressionH<V>, whereHis the composition ofFandG(i.e.type H<x> = F<G<x>>). Is this a desirable invariant? Is this simply an inaccurate mental model of what type aliases represent?