millsp / ts-toolbelt

👷 TypeScript's largest type utility library
https://millsp.github.io/ts-toolbelt/
Apache License 2.0
6.72k stars 148 forks source link

S.Length<S extends String> #158

Open MattRCole opened 3 years ago

MattRCole commented 3 years ago

🍩 Feature Request

Is your feature request related to a problem?

It is extremely difficult to derive the length of a string using typescript typings.

Unlike the relatively simple way one can get the length of an array:

type Length<T extends Array> = T['length']

type Test = Length<['a', 'b', 'c']> // Test = 3

The length of a string is very hard to determine

type Length<S extends String> = S['length']

type Test = Length<'abc'> // Test = number

Describe the solution you'd like

A first stab at a solution:

type EmptyString = ''
type NonEmptyString<S extends string> = S extends EmptyString
  ? never
  : S

type SingleChar<S extends string> = S extends `${NonEmptyString<infer X>}${NonEmptyString<infer Y>}`
  ? false
  : true

type Head<S extends string> =
  S extends `${infer X}${string}`
  ? SingleChar<X> extends true
    ? X
    : never
  : never

type Tail<S extends string> =
  S extends `${infer X}${infer Tail}`
  ? SingleChar<X> extends true
    ? Tail
    : never
  : never

type HasTail<S extends string> =
  Tail<S> extends EmptyString
  ? false
  : true

type __Length<S extends string, I extends any[] = []> = {
  0: __Length<Tail<S>, Next<I>>
  1: L.Length<I>
}[
  Tail<S> extends EmptyString
  ? 1
  : 0
]

type _Length<S extends string> =
  S extends EmptyString
  ? 0
  : __Length<S> extends infer X
    ? Increment<Cast<X, number>> // Increment here would probably be something like N.Plus<`${Cast<X, number>}`, '1', 'n'>
    : never

type Length<S extends string> =
  _Length<S> extends infer X
  ? Cast<X, number>
  : never

type Test0 = Length<'abc'> // Test0 = 3
type Test1 = Length<''>       // Test1 = 0

This type might be too "beefy" though and could probably be cut down. It also requires the use of TS 4.1.

Describe alternatives you've considered

I've considered putting in a pull request, but the proposed solution probably needs a lot work, so I've made this feature suggestion instead.

Teachability, Documentation, Adoption, Migration Strategy

A function description would probably be sufficient:

Returns the length of S as a Number

Edit: Clarity

millsp commented 3 years ago

Hey @MattRCole,

I would be all in for such a utility. You had a good practise there with the type system there, well done! But I think that we can indeed do shorter by shipping a Split type that will split on '' empty strings and then we can retrieve the length from there - just to keep it all more generic.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.