Created
August 15, 2022 21:54
-
-
Save sno2/7dac868ec6d11abb75250ce5e2b36041 to your computer and use it in GitHub Desktop.
A fast string length implementation in the TypeScript type system
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A logarithmic string length algorithm in the TypeScript type system utilizing | |
* a memoizing recursive type. Computes the length of any string with far | |
* superior performance than any other current implementations via a doubling | |
* cache and string patterns backing the checks. Works for any string with less | |
* than 10,000 characters due to the tuple size limits for the cache (in total). | |
* | |
* @author Carter Snook <[email protected]> github.com/sno2 | |
* @license https://unlicense.org/ (credit would be nice tho) | |
*/ | |
type TCache = [string, 0[]]; | |
type StringLengthUp< | |
T extends string, | |
$Acc extends 0[] = [], | |
$Cache extends TCache[] = [[`$${string}`, [0]]], | |
> = | |
// | |
$Cache extends [infer C extends TCache, ...infer $RestCache extends TCache[]] | |
? ( | |
`${C[0]}${C[0]}_` extends `$${string}$${infer $After}` | |
? `${C[0]}${$After}` extends `${infer $Before}_` ? $Before : never | |
: never | |
) extends infer $DoubleC extends string | |
? `$${T}` extends `${$DoubleC}${infer $Rest}` ? StringLengthUp< | |
$Rest, | |
[...$Acc, ...C[1], ...C[1]], | |
[[$DoubleC, [...C[1], ...C[1]]], ...$Cache] | |
> | |
: `$${T}` extends `${C[0]}${infer $Rest}` | |
? StringLengthUp<$Rest, [...$Acc, ...C[1]], $Cache> | |
: StringLengthDown<T, $Acc, $RestCache> | |
: never | |
: $Acc["length"]; | |
type StringLengthDown< | |
T extends string, | |
$Acc extends 0[], | |
$Cache extends TCache[], | |
> = $Cache extends | |
[infer C extends TCache, ...infer $RestCache extends TCache[]] | |
? `$${T}` extends `${C[0]}${infer $Rest}` | |
? StringLengthDown<$Rest, [...$Acc, ...C[1]], $Cache> | |
: StringLengthDown<T, $Acc, $RestCache> | |
: $Acc["length"]; | |
type StringLength<T extends string> = T extends "" ? 0 : StringLengthUp<T>; | |
type String9999 = "<a 9999 character long string>"; | |
type String9999Length = StringLength<String9999>; // 9999 |
@sno2 For reference, I included a variant of your work in one of my libraries: https://www.npmjs.com/package/@coderspirit/nominal-inputs (and left the proper references to your gist 😃 )
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @sno2 , I've found a small bug in your type, but it's easy to fix:
Fix:
Moreover, if we want to make it work better with "tweaked types" (the result of complex type guards), we could refine it to be: