TypeScript での型バリデータの構成


この記事では、いくつかの型検証手法を見つけることができます.

簡単な関数から始めましょう

関数の引数は常に何らかの CSS 値である必要があるとします.例: 100px、10rem、50% など ...

まず、値が許可された測定単位で終わっているかどうかを確認する必要があります.

type Units = 'px' | 'rem' | '%';


これで、測定単位を数値と単位の 2 つの部分に分割できるはずです.


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


一般的なバリデータを書きましょう:

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'>> // []


Validator からの戻り値の型として配列が必要な理由を説明します.

試してみよう

const foo = <T,>(arg: T, ...validation: Validator<IsValidCSS<T>>) => {}

foo('10px'); // error


引数はリテラル 10px ではなく文字列に推論されるため、まだ機能しません.

これを修正するには、ジェネリック型に追加の制約を適用する必要があります.

const foo = <T extends string>(arg: T, ...validation: Validator<IsValidCSS<T>>) => {}

foo('10px'); // ok
foo('10%'); // ok
foo('10p'); // error


複数のバリデーターを適用することは可能ですか?

CSS で 99 の使用が許可されていないとします.

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

Validator が失敗するたびに、 [never] が返されます.残りの演算子を使用しているため、 never に評価されます.

したがって、 Validator が失敗した場合、TS は 2 番目の引数として never を期待します.

それで全部です.