Skip to content

Instantly share code, notes, and snippets.

@Fasteroid
Last active May 30, 2025 20:14
Show Gist options
  • Save Fasteroid/1cf4a20bb6ebc7be1d83ed265c8b22b1 to your computer and use it in GitHub Desktop.
Save Fasteroid/1cf4a20bb6ebc7be1d83ed265c8b22b1 to your computer and use it in GitHub Desktop.
Some random, probably inadvised things you can do with TypeScript's type system.
/**
* WARNING!
* Just because you can doesn't mean you should.
*
* It's not very hard to create types with exponential (or worse!) time complexity.
* These can create serious issues at build time, such as running out of memory.
* Use caution when doing metaprogramming like this. Nothing is truly free.
*
* This file is best viewed in an environment with intellisense.
*/
/**
* Generates a tuple of *{@link T}* that is *{@link N}* elements long.
* @concepts https://stackoverflow.com/questions/52489261/can-i-define-an-n-length-tuple-type
*/
type TupleOfLength<N extends number, T = any, RETURN extends T[] = []> =
RETURN['length'] extends N ? // if we reach the desired length
RETURN // return RETURN
:
TupleOfLength<N, T, [T, ...RETURN]> // else try again with a tuple 1 element longer
//
/**
* Generates a tuple [0 ... *{@link N}*-1]
* @concepts {@linkcode TupleOfLength}
*/
type AscendingTuple<N extends number, RETURN extends number[] = []> =
RETURN['length'] extends N ?
RETURN
:
AscendingTuple<N, [
...RETURN, // flip the order of these
RETURN['length'] // to make it descending
]>
//
/**
* *{@link A}* + *{@link B}*
* #### Caveats:
* - no negative numbers
* - O(n) complexity
*/
type Add<A extends number, B extends number> = [...TupleOfLength<A>, ...TupleOfLength<B>]['length']
//
/**
* Gets the first element of a tuple.
*/
type FirstOf<TUPLE extends unknown[]> =
TUPLE extends [infer FIRST, ...infer REMAINING] ? // flip the order to get the last
FIRST // use REMAINING to get the rest instead
:
never
//
/**
* Finds the sum of a tuple of numbers.
* Note that now, working with tuples directly for mathematical operations is now more convenient than using actual numbers.
* #### Caveats:
* - O(n^2) complexity
* @concepts {@linkcode FirstOf}, {@linkcode TupleOfLength}
*/
type Sum<NUMS extends number[]> = _Sum<NUMS>['length']
type _Sum<NUMS extends number[], RETURN extends unknown[] = []> =
NUMS['length'] extends 0 ?
RETURN
:
NUMS extends [infer FIRST extends number, ...infer REMAINING extends number[]] ?
_Sum<REMAINING, [...RETURN, ...TupleOfLength<FIRST>]>
:
never
//
/**
* Keys of *{@link T}* which hold *{@link KIND}*
* @concepts https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html#key-remapping-in-mapped-types
*/
type KeysHolding<T, KIND = any> = keyof // keys of the following:
{ [K in keyof T // a type with the keys of T
as ( T[K] extends KIND ? // but try to cast those keys to KIND
K
:
never // producing 'never' in the 'as' clause filters the key out
)
]: T[K] // use the original value types of T where applicable
}
//
/**
* A precursor to converting a tuple to a mapped type.
* This does it for the last item of the tuple only.
* @concepts {@linkcode FirstOf}
*/
type ObjectWithLastKeyOf<TUPLE extends unknown[]> =
TUPLE extends [...infer REMAINING, infer LAST] ?
{ [K in TUPLE['length']]: LAST } // 'in' is similar to '==' here
:
never
//
type _TupleToMap<
STACK extends {
tup: unknown[] // the tuple to iterate
map: { [i: number]: unknown } // the map to return
},
> =
STACK['tup'] extends [...infer REST, infer LAST] ? // pop LAST off `tup` if we can
_TupleToMap<{
tup: REST, // pass the remaining to the next iteration
map: STACK['map'] & { // the previous iteration's map...
[K in STACK['tup']['length']]: LAST // ...plus the element we just popped from `tup`
}
}>
:
STACK['map'] // we couldn't pop, so we must be done
//
/**
* Converts a tuple to a mapped type with the same keys.
* ### Caveats
* - O(n) complexity
* @example ```ts
* type From = [1, "two", {value: 3}]
* type To = {
* 1: 1,
* 2: "two",
* 3: {value: 3}
* }
* ```
* @concepts {@linkcode FirstOf}, {@linkcode ObjectWithLastKeyOf}
*/
type TupleToMap<TUPLE extends unknown[]> = _TupleToMap<{
tup: TUPLE,
map: {}
}>
// WARNING: The following type is actually very useful!
/**
* True if T is a positive number, false otherwise.
* @author Fasteroid
*/
type Positive<T extends number> = `${T}` extends `-${number}` ? false : true;
/**
* Checks if a type is `never`... which is harder than you think it is!
* @concepts https://www.webdevtutor.net/blog/typescript-check-if-type-is-never
*/
type IsNever<T> = [T] extends [never] ? true : false;
// Here's an approach for adding sidechannels to types for things like metadata, etc.
// I have no idea if this is a useful technique
class _name<T> { private _name?: };
type NamelessThing = { what: string };
type NamedThing = NamelessThing & _name<"Thing">;
type ThingName = NamedThing extends _name<infer T> ? T : never; // "Thing"
/**
* If both types are known, ensures they match, otherwise returns as much as is known.
*
* This can get you out of tricky binds where TypeScript's default inferencing is too strict.
*/
type SoftMatch<A, B> =
[unknown] extends [A] ? // A is unknown
[unknown] extends [B] ? // B is unknown
unknown
: // B is known
B
: // A is known
[unknown] extends [B] ? // B is unknown
A
: // B is known
B extends A ? A extends B ? A : never : never // assert they are the same
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment