reactjs - Cum să creezi un tip TypeScript care validează formatul unui șir de caractere? Adică să verifice dacă șirul este o proprietate CSS de lungime validă


Deci am un proiect React în care componenta primește o proprietate height. Acea proprietate este folosită pentru a determina proprietățile CSS ale componentei (folosesc biblioteca Emotion)

De exemplu,

render() {
  const styles = css`
    height: ${this.props.height};
  `;

  return (
    <div style={styles}>
      ...
    </div>
  );
}

Am un tip pentru înălțime, care este în prezent

interface Props {
  height: number | string;
}

În loc să am un tip care verifică dacă înălțimea este un șir de caractere, vreau să creez un tip care validează unitatea înălțimii dacă este un șir de caractere.

De exemplu,

10px este o proprietate validă, deci nu apare eroare în TypeScript.

10xp va genera o eroare TypeScript.

Există o modalitate de a crea un tip care să verifice că prima parte este un număr, iar a doua parte este una dintre aceste valori?

type CssAbsoluteUnit = "cm" | "mm" | "in" | "px" | "pt" | "pc";

type CssRelativeUnit = "em" | "ex" | "ch" | "rem" | "vw" | "vh" | "vmin" | "vmax" | "%";

Aș vrea să fac asta într-un mod în care compilatorul TypeScript să genereze o eroare, așa că utilizarea doar a regex-ului pentru validare la redare nu rezolvă cu adevărat problema.

Răspuns #1

Acest lucru poate fi realizat cu Tipul Literal al Șablonului, deși există unele limitări legate de ceea ce poate trece incorect drept valid:

type Unit = '%' | 'px' | 'em' | 'vh' | 'vw'

type HeightProp = `${number}${Unit}`


const valid: WidthValue[] = ['10%', '100px', '100em', '100vh', '100vw'] // Valid
const invalid: WidthValue[] = ['10leagues', 'one-hundred-px'] // Error
const falseNegative: WidthValue[] = ['10 px', '0e1px'] // Invalid but passes

Acest exemplu nu este exhaustiv, dar conceptul ar putea fi extins pentru a acoperi o gamă mai largă de proprietăți CSS și valori valide.

Răspunsul #2

Din păcate, asta sună ca ceva care se determină în timpul rulării după ce compilatorul și-a făcut treaba. Totuși, dacă știi din timp ce valori urmează să fie transmise, poți să creezi o interfață și să implementezi clasele pentru tipurile care urmează să fie transmise.

Ceva de genul acesta:

interface CssUnit {
  getValue(): string;
}

class PxUnit implements CssUnit {
  private unit: string;

  constructor(value: number) {
    this.unit = value + "px";
  }

  getValue(): string {
    return this.unit;
  }
}

atunci,

interface Props {
  height: CssUnit;
}

Tot ce ar trebui să faci este să transmiți una dintre clasele tale implementate.

Sper că asta ajută!