Cuarteto 9: Allegro | Mecanografiado

Cuando quartetse creó la biblioteca para la validación de datos , se establecieron los siguientes puntos de referencia:



En este artículo, me gustaría considerar quartetcentrarme en TypeScript.


Motivación


Trabajamos en nuestros proyectos usando TypeScript. Por lo tanto, cuando creé esta biblioteca, quería que la persona que conoce TypeScript no aprenda cuarteto como algo completamente nuevo para él, sino que reconozca en esta biblioteca lo que ya sabe.


Protectores de tipo definidos por el usuario


Considera un ejemplo. Solicitamos datos sobre el usuario con la API. Suponemos que son del siguiente tipo:


interface User {
  id: string;
  name: string;
  gender: "male" | "female";
  age: number;
  phoneBook: {
    [name: string]: string;
  };
}

Lo que quiero obtener de la función de validación:


const probablyUser: unkown = { ... }

if (checkUser(probablyUser)) {
    // probablyUser has type User
    console.log(probablyUser.name)
} else {
    // probablyUser has type unkown
    throw new Error('Probably User has not type User')
}

Para lograr este objetivo, se utilizan protectores de tipo definidos por el usuario .


Es decir, la declaración de función debería verse así:


function checkUser(probablyUser: any): probablyUser is User {
  // ...
}

Usemos quartetpara crear tal función:


import { v } from "quartet";

const checkUser = v({
  id: v.string,
  name: v.string,
  gender: ["male", "female"],
  age: v.number
  phoneBook: {
    [v.rest]: v.string,
  }
});

Después de escribir dicho código, obtenemos una función que no es TypeGuard:


chechUser: (value: any) => boolean;

Para que sea TypeGuard es necesario indicar declarativamente qué tipo será validado por esta función. Esto se hace así:


const checkUser = v<User>({
  // ...
});

Finalmente:


chechUser: (value: any) => value is User

Hay dos puntos con respecto a este artículo:


Garantía


El hecho mismo de que el desarrollador pueda indicar qué tipo es validado por el circuito puede alertar, porque es muy posible escribir así:


const checkNumber = v<number>({ name: v.string });
// checkNumber: (value: any) => value is number

{ name: string }, , number.


. ( ) , — "" .


, .


, v. :


const v: <T>(schema: Schema) => (value: any) => value is T;

.


, TypeGuard .


:


const v: <T = any>(schema: Schema) => (value: any) => value is T;

, — never:


const checkNumber = v(v.number);
const value: any = "123";

if (!checkNumber(value)) {
  // value has type never
}

, , .


, T any , T === any, .


- :


const v: <T = any>(
  schema: Schema
) => IfAny<T, (value: any) => boolean, (value: any) => value is T>;

type IfAny<T,A,B> = // ...

— , :


T — any


:


type IfAny<T, A, B> = true extends T
  ? "1" extends T
    ? 1 extends T
      ? {} extends T
        ? (() => void) extends T
          ? null extends T
            ? A
            : B
          : B
        : B
      : B
    : B
  : B;

, , : boolean | number | string | object | function | null any.


TypeScript


, , TypeScript'a .


@hapi/joi ajv, User.


Text Compare .


quartet


const checkUser = v({
  id: v.string,
  name: v.string,
  gender: ["male", "female"],
  age: v.number
  phoneBook: {
    [v.rest]: v.string,
  }
})

imagen


- : 24


hapi/joi


const schema = j.object({
  id: j.string().required(),
  name: j.string().required(),
  gender: j
    .string()
    .valid("male", "female")
    .required(),
  age: j.number().required(),
  phoneBook: j.object().pattern(/.*/, j.string())
});

imagen


, , 118 .


ajv


const checkUser = a.compile({
  type: "object",
  required: ["id", "name", "gender", "age", "phoneBook"],
  properties: {
    id: { type: "string" },
    name: { type: "string" },
    gender: { type: "string", enum: ["male", "female"] },
    phoneBook: {
      type: "object",
      additionalProperties: {
        type: "string"
      }
    }
  }
});

imagen


, 146 .


:


imagen


TypeScript. , , , .



TypeGuard — , .


TypeScript . — .


All Articles