Home > Mobile >  Typescript custom compile time validation types
Typescript custom compile time validation types

Time:09-22

Is there any way that we can define types that are checked during typescript compile time only. I want user to define a value to variable (ie not going to change on runtime) and i want to check if that value matches some criteria. For example, user need to set numeric value but cannot set less then 5, or set an string with specific format or validate with regex eg email. or string that must follow specific format or it can be pass through some condition and pass the test or else throw the error with defined message Is there any way to achieve this in typescript??

CodePudding user response:

  1. Numeric value

You are allowed to create a union of allowed numeric values. But then you have to set maximum allowed value and compute range. See this answer and my article.

Here you have small example:

type MAXIMUM_ALLOWED_BOUNDARY = 999

type ComputeRange<
    N extends number,
    Result extends Array<unknown> = [],
> =
    (Result['length'] extends N
        ? [...Result, Result['length']][number]
        : ComputeRange<N, [...Result, Result['length']]>
    )



type NumberRange = ComputeRange<MAXIMUM_ALLOWED_BOUNDARY>

type Except<N extends number, CustomRange extends number> = Exclude<CustomRange, N>

type GreaterThanFive = Except<ComputeRange<5>, NumberRange>

const less: GreaterThanFive = 2 //expected error
const greater: GreaterThanFive = 6 // ok

Playground

  1. String with specific format.

You can use template literal strings

type SpecificFormat = `${string}-${string}`

type StringDigit = `${number}`

const str: SpecificFormat = 'hello-world' // ok
const str2: SpecificFormat = 'hello world' // expected error

const strDigit: StringDigit = '42' // ok
const strDigit2: StringDigit = '42a' // expected error

However, if you want to apply more advanced restrictions, for instance check whether it is a valid HEX value or email you need to use duplicate variable value in a type or use extra dummy function. See example:

type ComputeRange<
    N extends number,
    Result extends Array<unknown> = [],
> =
    (Result['length'] extends N
        ? Result[number]
        : ComputeRange<N, [...Result, Result['length']]>
    )

type HexNumber = `${ComputeRange<10>}`
type HexString =
    | 'A'
    | 'B'
    | 'C'
    | 'D'
    | 'E'
    | 'F'
    | 'a'
    | 'b'
    | 'c'
    | 'd'
    | 'e'
    | 'f'

type Hex = `${HexNumber}` | HexString;

type StringLength<
    Str extends string,
    Acc extends string[] = []
> =
    (Str extends `${infer S}${infer Rest}`
        ? StringLength<Rest, [...Acc, S]>
        : Acc['length'])

type ValidateLength<
    Str extends string,
    Length extends number
> =
    (StringLength<Str> extends Length
        ? Str
        : never)

type WithHash<T extends string> = `#${T}`

type ValidateHex<
    Color extends string,
    Cache extends string = '',
> =
    Color extends `${infer A}${infer Rest}`
    ? (A extends ''
        ? WithHash<Cache>
        : (A extends Hex
            ? ValidateHex<Rest, `${Cache}${A}`>
            : never)
    ) : WithHash<Cache>

const hex: ValidateHex<'ffffff'> = '#ffffff' // ok
const hex2: ValidateHex<'fffffz'> = '#ffffff' // expected error

Playground

If you want to validate function arguments you can check this answer, this answer or my article

If you are interested in email validation you can check this answer or my article

Also it is possible to apply restriction where all chars should be lowercased or uppercased, fir this purpose you can use built in intrinsic utility types:


// credits goes to @jcalz https://stackoverflow.com/questions/68963491/define-a-typescript-type-that-takes-a-lowercase-word#answer-73732194
let str: Lowercase<string>;
str = "abc"; // okay
str = "DEF"; // error in TS4.8 

However, it works only for TS 4.8 . See my article or this answer

  • Related