typescript: cum să creezi un Tip care poate converti un șir de caractere în majuscule


Vreau să creez un Tip în TypeScript care poate converti un șir de caractere în majuscule:

type _Upper<T> = ...

_Upper<'abc'> // 'ABC'

poate cineva să îmi spună asta?

Răspuns #1

Modul corect de a face acest lucru este să folosești pur și simplu tipul de utilitate existent Uppercase<T>. Acesta este un tip intrinsec, așa cum a fost introdus în microsoft/TypeScript#40580, ceea ce înseamnă că este implementat chiar în compilator; poți să te uiți în checker.ts aproximativ la linia 15.085:

case IntrinsicTypeKind.Uppercase: return str.toUpperCase()

Asta înseamnă că ai putea defini tipul tău personalizat _Upper<T> în felul următor:

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

Da, aceasta este o definiție trivială. Dar are avantajul de a fi simplu de definit și de a funcționa conform algoritmului de mapare a cazurilor din standardul Unicode:

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 Хорошо = "Я НЕ ЗНАЮ"

Dacă nu vrei să folosești Uppercase<T>, poți folosi tipuri literale șablon recursive pentru a implementa ceva tu însuți. Dar reține că pentru a face acest lucru corect este necesar să păstrezi o mapare a tuturor caracterelor Unicode care trebuie schimbate atunci când sunt convertite în majuscule. Cel puțin, aceasta ar fi o mapare destul de mare (peste 2.000 de caractere, cred).

Dacă tot ce te interesează sunt cele 26 de caractere ale alfabetului latin, atunci poți să o scrii astfel:

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;

Deci Uppers este tipul de mapare de la litere mici (chei) la litere mari (valori). Tipul _Upper<T> analizează șirul T caracter cu caracter. Dacă caracterul F este literă mică (F extends keyof Uppers), atunci îl convertește în literă mare (Uppers[F]), altfel îl lasă neschimbat (F). Este o definiție recursive terminală (folosind parametrul de tip A ca acumulator), astfel încât în TypeScript 4.5 și versiuni ulterioare, acest lucru va funcționa pentru șiruri literale destul de lungi.

Să o testăm:

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

Ei bine, primele două sunt bune.

Dar ultimul nu funcționează; șirul de litere chirilice mici rămâne neschimbat. Acest lucru ar putea fi corectat, dacă este necesar, prin completarea tipului Uppers cu alfabetul chirilic. Algoritmul de bază aici, de a analiza șirul și de a căuta fiecare caracter într-o mapare, este probabil cel mai bun lucru pe care îl putem face. Ei bine, cel mai bun ar fi să folosim Uppercase<T> încorporat. Dar dacă Uppercase<T> nu ar exista, atunci un tip literal de șablon condițional recursiv ca acesta ar fi rezonabil.

Link de teren de joacă către cod