9

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.

davidhu
  • 9,523
  • 6
  • 32
  • 53
  • you can use regex type with ending of the string in one of your desired unit values – Rikin Sep 10 '19 at 15:53
  • 1
    Possible duplicate of [How to define a regex-matched string type in Typescript?](https://stackoverflow.com/questions/51445767/how-to-define-a-regex-matched-string-type-in-typescript) – trixn Sep 10 '19 at 16:06
  • As far as I know there is not way to define a such a type. There is a [proposal](https://github.com/Microsoft/TypeScript/issues/6579) but there doesn't seem to be any effort on including such a feature in a near future release. – trixn Sep 10 '19 at 16:06
  • @Rikin I'm not aware of such a type in typescript. Could you provide a source for that? – trixn Sep 10 '19 at 16:12
  • `const regex: RegExp = /my_regex/` actually I wrongly typed as `type` availability – Rikin Sep 10 '19 at 16:22

2 Answers2

4

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.

monners
  • 5,174
  • 2
  • 28
  • 47
2

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!