Quarteto 9: Allegro | TypeScript

Quando a biblioteca para validação de dados quartetfoi criada, os seguintes pontos de referência foram definidos:



Neste artigo, gostaria de considerar o foco quartetno TypeScript.


Motivação


Trabalhamos em nossos projetos usando o TypeScript. Portanto, quando criei esta biblioteca, queria que a pessoa que conhecia o TypeScript não aprendesse o quarteto como algo completamente novo para ele, mas reconhecesse nessa biblioteca o que ele já sabe.


Protetores de tipo definidos pelo usuário


Considere um exemplo. Solicitamos dados sobre o usuário com a API. Assumimos que eles sejam do seguinte tipo:


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

O que eu quero obter da função de validação:


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 atingir esse objetivo, são utilizadas proteções de tipo definido pelo usuário .


Ou seja, a declaração da função deve ficar assim:


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

Vamos usar quartetpara criar essa função:


import { v } from "quartet";

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

Depois de escrever esse código, obtemos uma função que não é TypeGuard:


chechUser: (value: any) => boolean;

Para torná-lo TypeGuard, é necessário indicar declarativamente que tipo será validado por esta função. Isso é feito assim:


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

Eventualmente:


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

Existem dois pontos em relação a este item:


garantia


O próprio fato de o desenvolvedor poder indicar qual tipo é validado pelo circuito pode alertar, porque é bem possível escrever assim:


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,
  }
})

imagem


- : 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())
});

imagem


, , 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"
      }
    }
  }
});

imagem


, 146 .


:


imagem


TypeScript. , , , .



TypeGuard — , .


TypeScript . — .


All Articles