|
// Count<n> = [0, 1, ..., n] |
|
type Count<Len extends number, Arr extends number[] = []> = |
|
Arr['length'] extends Len ? Arr : Count<Len, [...Arr, Arr['length']]> |
|
|
|
// Enumerate<n> = 0 | 1 | ... | n |
|
type Enumerate<Len extends number, Arr extends number[] = []> = |
|
Arr['length'] extends Len ? Arr[number] : Enumerate<Len, [...Arr, Arr['length']]> |
|
|
|
// Tail<[a, b, c, ...]> = [b, c, ...] |
|
type Tail<T extends any[]> = T extends [ any, ...infer End ] ? End : []; |
|
|
|
// Pred<n> = n-1 |
|
type Pred<A extends number> = Tail<[...Count<A>]>['length'] |
|
|
|
// ByteNumber = 0 | 1 | ... | 255 |
|
type ByteNumber = Enumerate<256> |
|
|
|
// IPv4String = `${ByteNumber}(.${ByteNumber})*` |
|
type IPv4String<T extends string, V = T> = |
|
T extends `${ByteNumber}.${infer R}` |
|
? IPv4String<R, V> |
|
: T extends `${ByteNumber}` |
|
? V |
|
: never |
|
|
|
const testIPv4String = <T extends string>(n: IPv4String<T>) => n; |
|
testIPv4String('105') |
|
// testIPv4String('142.256') // never |
|
testIPv4String('234.7.183.32.51.3.153') |
|
// testIPv4String('0.0.0.0.00') // never |
|
|
|
// IPv4Str = `${ByteNumber}.${ByteNumber}.${ByteNumber}.${ByteNumber}` |
|
type IPv4Str<T extends string, i extends number = 3, V = T> = |
|
i extends 0 |
|
? (T extends `${ByteNumber}` ? V : never) |
|
: i extends 1 |
|
// Generic subtraction works, but we have one case for each number |
|
? (T extends `${ByteNumber}.${infer R}` ? IPv4Str<R, Pred<i>, V> : never) |
|
: i extends 2 // Try with `i extends 2 | 3` and notice how it fails |
|
? (T extends `${ByteNumber}.${infer R}` ? IPv4Str<R, Pred<i>, V> : never) |
|
: i extends 3 |
|
? (T extends `${ByteNumber}.${infer R}` ? IPv4Str<R, Pred<i>, V> : never) |
|
: never |
|
|
|
const testIPv4Str = <T extends string>(n: IPv4Str<T>) => n; |
|
// testIPv4Str("65") // never |
|
testIPv4Str("110.15.1.189") |
|
// testIPv4Str("0.0.0.0") // never |
|
testIPv4Str("1.2.3.4.5") |
|
|
|
// IPv4 = `${ByteNumber}.${ByteNumber}.${ByteNumber}.${ByteNumber}` |
|
type IPv4<T extends string, i extends number = 3, V = T> = |
|
i extends 0 |
|
? (T extends `${ByteNumber}` ? V : never) |
|
// IDK, is there some kind of caching or other optimization here? |
|
: T extends `${ByteNumber}.${infer R}` |
|
? IPv4<R, Pred<i>, V> |
|
: never |
|
|
|
const testIPv4 = <T extends string>(n: IPv4<T>) => n; |
|
// All fail |
|
testIPv4("65") |
|
testIPv4("110.15.1.189") // Should work :( |
|
testIPv4("0.0.0.0") |
|
testIPv4("1.2.3.4.5") |