maninak / ts-xor

Compose object types containing mutually exclusive keys, using this generic Typescript utility type.
https://app.radicle.at/nodes/seed.radicle.at/rad:z3nP4yT1PE3m1PxLEzr173sZtJVnT
MIT License
106 stars 5 forks source link

feat(xor): support xoring more than two types (up to 200!) #27

Closed maninak closed 1 year ago

maninak commented 1 year ago

This approach leverages meta-programming, specifically the TypeScript Compiler API to generate the XOR type according to a new algorithm specified in xorFactory.js. Here's the resulting XOR type (205_708 chars ! :sweat_smile:):

image

This new algorithm allows us to extend the count of generic params that XOR accepts up to seemingly any arbitrary number (tentatively 200). Note that this new algorithm doesn't produce a recursive type. It instead produces a union, each member of which is designed to perform efficient "early bail-out" before performing the complex evaluation that corresponds to it, unless necessary (related param is defined). That's by design, since solutions involving recursive types have higher memory cost and quickly hit the TypeScript compiler's resolve stack limit (max 6 generic params during my testing). In contrast, the code describing a recursive type implementing XOR is dramatically less and comes with much less processing overhead.

Perhaps the next evolution could be a type that uses a recursive eval for up to 6 params (with early bail-out) and the "unfolded" solution that this PR brings when using more params.

The performance overhead of having (up to) 200 (10000% increase) possible generic type parameters was measured (roughly) to be only around +25% compared to the current implementation supporting 2 params. That's despite the type's immense definition size (>200KB of uglified TS code) and it processing two orders of magnitude more interdependent operands. Having 100 params brings the perf overhead only down to ~+18%. The community has hinted there might be need for high param counts for some users. Regardless, tuning the number of params supported is as easy as adjusting xorParamCount in xorFactory.js :100:. And given that bespoke DX, any user who needs much more than this count of params can easily clone the repo and generate their own larger-param-capacity XOR type (just adjust the constant's value and run npm run codegen in your shell) and copy-paste it in their project.

Note that this PR brings a non-breaking change by extending the existing API, which is important given the extensive userbase (>5M downloads) of ts-xor!

Closes #22