Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a typescript type that validates string format? i.e. checks is string is valid css length property

So I have a react project where the component takes a height prop. That prop is used to determine the css properties for the component (I use the emotion library)

For example,

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

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

I have a type for height, which is currently

interface Props {
  height: number | string;
}

Instead of having a type that checks if the height is a string, I want to create a type that validates the unit of height if it is a string.

For example,

10px is a valid prop, so no typescript error.

10xp will throw a typescript error.

Is there a way to create a type that checks that the first part is a number, and the second part is one of these values?

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

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

I'd like to do this in a way that typescript compiler will throw an error, so using just regex to validate on render doesn't really do the job.

like image 369
davidhu Avatar asked Nov 07 '22 13:11

davidhu


2 Answers

Unfortunately, this sounds like something that is determined during runtime after the compiler has done it's job. However, if you know ahead of time, what values that are going to be passed you can create an interface and implement the classes for the types that are going to be passed in.

Something like this:

interface CssUnit {
  getValue(): string;
}

class PxUnit implements CssUnit {
  private unit: string;

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

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

then,

interface Props {
  height: CssUnit;
}

All you would have do to is pass in one of your implemented classes.

Hope that helps!

like image 64
gabeshoodie Avatar answered Nov 15 '22 05:11

gabeshoodie


This can be achieved with the Template Literal Type, although there are some limitations around what may incorrectly pass as valid:

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

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

This example isn't exhaustive, but the concept could be expanded into covering a wider range of CSS properties and valid values.

like image 39
monners Avatar answered Nov 15 '22 05:11

monners