4

I want to create a Type in typescript which can convert a string to uppercase:

type _Upper<T> = ...

_Upper<'abc'> // 'ABC'

can anyone tell me this?

5
  • 3
    You mean this? typescriptlang.org/docs/handbook/2/… Commented Jan 4, 2022 at 14:22
  • @Calculuswhiz is right; you don't have to write this type, it already exists as Uppercase<T>. If that intrinsic type didn't exist you could presumably build a template literal parser type that converts some hardcoded alphabet to uppercase, but it would probably be prohibitively expensive to write one that worked for all of Unicode. Observe this. Does that answer work for you or are we missing something? Commented Jan 4, 2022 at 17:14
  • hi Calculuswhiz: you are right, however I want to write a Type UpperCase BY MYSELF. The link provided by @jcalz help me a lot, but is that the most suitable method? Commented Jan 5, 2022 at 9:05
  • 1
    @fawinell I don't know how to answer if it's "the most suitable method" unless you can articulate why do you want to write it yourself instead of using the native Uppercase<T>. What are your requirements? Is this just a thought experiment for fun? Or do you have some kind of use case? Commented Jan 5, 2022 at 19:02
  • hi @jcalz: thanks for your advice, yes it's just a thought experiment. Commented Jan 6, 2022 at 7:08

1 Answer 1

9

The correct way to do this is to just use the existing Uppercase<T> utility type. This is an intrinsic type as introduced in microsoft/TypeScript#40580, which means that it is implemented in the compiler itself; you can look at checker.ts at approximately line 15,085:

case IntrinsicTypeKind.Uppercase: return str.toUpperCase()

That means you could define your custom _Upper<T> type like this:

type _Upper<T extends string> = Uppercase<T>;

Yes, that's a trivial definition. But it has the advantage of being simple to define and working according to the Unicode standard case mapping algorithm:

type ABC = _Upper<'abc'>
// type ABC = "ABC"

type Sentence = _Upper<"The quick brown fox jumps over the lazy dog.">;
// type Sentence = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG."

type Хорошо = Uppercase<"я не знаю">;
// type Хорошо = "Я НЕ ЗНАЮ"

If you don't want to use Uppercase<T>, you can use recursive template literal types to implement something yourself. But note that to do so accurately requires that you keep a mapping of all Unicode characters which need to be changed when converted to upper case. At the very least that would be a fairly large mapping (over 2,000 characters I think).

If all you care about are the 26 characters of the Latin alphabet, then you can write it like this:

interface Uppers {
  a: "A", b: "B", c: "C", d: "D", e: "E", f: "F", g: "G", 
  h: "H", i: "I", j: "J", k: "K", l: "L", m: "M",
  n: "N", o: "O", p: "P", q: "Q", r: "R", s: "S", t: "T", 
  u: "U", v: "V", w: "W", x: "X", y: "Y", z: "Z"
}

type _Upper<T extends string, A extends string = ""> =
  T extends `${infer F}${infer R}` ?
  _Upper<R, `${A}${F extends keyof Uppers ? Uppers[F] : F}`> : A;

So Uppers is the mapping type from lowercase (keys) to uppercase (values). The _Upper<T> type parses the string T character-by-character. If the character F is lowercase (F extends keyof Uppers) then it converts it to uppercase (Uppers[F]), otherwise it leaves it alone (F). It's a tail-recursive definition (using the A type parameter as an accumulator) so in TypeScript 4.5 and above, this will work for quite long string literals.

Let's test it:

type ABC = _Upper<'abc'>
// type ABC = "ABC"

type Sentence = _Upper<"The quick brown fox jumps over the lazy dog.">;
// type Sentence = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG."

type Плохо = _Upper<"я не знаю">;
// type Плохо = "я не знаю" // oops

Well, the first two are good.

But the last one doesn't work; the lowercase Cyrillic string is unaffected. This could be fixed, if necessary, by augmenting the Uppers type with the Cyrillic alphabet. The basic algorithm here, parsing the string and looking up each character in a mapping, is probably the best we can do. Well, the best would be to use the built-in Uppercase<T>. But if Uppercase<T> didn't exist, then a recursive conditional template literal type like this would be reasonable.

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.