17
Composition of type validators in TypeScript
In this article, You can find some type validation techniques.
Let's start with simple function
Assume, our function argument should be always some CSS value. For example: 100px, 10rem, 50% etc ...
First of all we should check if value ends with some allowed measure units:
type Units = 'px' | 'rem' | '%';
Now, we should be able to split our measure units into two parts: number and unit
type Units = 'px' | 'rem' | '%';
type IsValidCSS<T extends string> = T extends `${number}${Units}` ? true : false;
type Result = IsValidCSS<'10px'> // true
type Result2 = IsValidCSS<'10p'> // false
Lets write generic validator:
type Units = 'px' | 'rem' | '%';
type IsValidCSS<T extends string> = T extends `${number}${Units}` ? true : false;
type Validator<T extends boolean> = T extends true ? [] : [never];
type Test = Validator<IsValidCSS<'10px'>> // []
Please, give me a minute, I will explain why we need an array as a return type from Validator
Let's try it
const foo = <T,>(arg: T, ...validation: Validator<IsValidCSS<T>>) => {}
foo('10px'); // error
Still does not work, because argument is infered to string instead of literal 10px
.
In order to fix it, we should apply additional constraints to the generic type:
const foo = <T extends string>(arg: T, ...validation: Validator<IsValidCSS<T>>) => {}
foo('10px'); // ok
foo('10%'); // ok
foo('10p'); // error
Is it possible to apply several validators?
Assume, we are not allowed to use 99
in our CSS
type Units = 'px' | 'rem' | '%';
type IsValidCSS<T> = T extends `${number}${Units}` ? true : false;
type StringNumber<T extends number> = `${T}`;
type IsAllowedNumber<T> =
T extends `${infer Num}${Units}`
? Num extends StringNumber<99>
? false
: true
: false;
type Validator<T extends boolean> =
T extends true
? []
: ['Dear developer, please use valid CSS values'];
const foo = <T extends string>
(
arg: T,
...validation: [...Validator<IsValidCSS<T>>, ...Validator<IsAllowedNumber<T>>]
) => { }
foo('100px'); // ok
foo('99px'); // expected error
Each time, when Validator
fails, it returns [never]
and because we are using rest operator it evaluates to never
.
So if Validator
has failed, TS expects second argument which is never
.
That's all.
17