рдЪреМрдХрдбрд╝реА 9: рдПрд▓реЗрдЧреНрд░реЛ | рдЯрд╛рдЗрдкрдкреНрд░рддрд┐

рдЬрдм рдбреЗрдЯрд╛ рд╕рддреНрдпрд╛рдкрди рдХреЗ рд▓рд┐рдП рдкреБрд╕реНрддрдХрд╛рд▓рдп quartetрдмрдирд╛рдпрд╛ рдЧрдпрд╛ рдерд╛, рддреЛ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕реНрдерд▓ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд┐рдП рдЧрдП рдереЗ:



рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ quartetрдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░рдиреЗ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ред


рдкреНрд░реЗрд░рдгрд╛


рд╣рдо рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдкрд░ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рдЬрдм рдореИрдВрдиреЗ рдЗрд╕ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд┐рдпрд╛, рддреЛ рдореИрдВ рдЪрд╛рд╣рддрд╛ рдерд╛ рдХрд┐ рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЬрд╛рдирдиреЗ рд╡рд╛рд▓рд╛ рд╡реНрдпрдХреНрддрд┐ рдЪреМрдХрдбрд╝реА рди рд╕реАрдЦреЗ, рдХреНрдпреЛрдВрдХрд┐ рдЙрд╕рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдирдпрд╛ рд╣реЛ, рд▓реЗрдХрд┐рди рдЗрд╕ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдореЗрдВ рдкрд╣рдЪрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рд╡рд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдХреНрдпрд╛ рдЬрд╛рдирддрд╛ рд╣реИред


рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛-рдкрд░рд┐рднрд╛рд╖рд┐рдд рдкреНрд░рдХрд╛рд░ рдХреЗ рдЧрд╛рд░реНрдб


рдПрдХ рдЙрджрд╛рд╣рд░рдг рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВред рд╣рдо рдПрдкреАрдЖрдИ рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдбреЗрдЯрд╛ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рддреЗ рд╣реИрдВред рд╣рдо рдорд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рд╡реЗ рдирд┐рдореНрди рдкреНрд░рдХрд╛рд░ рдХреЗ рд╣реИрдВ:


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

рдореИрдВ рд╕рддреНрдпрд╛рдкрди рд╕рдорд╛рд░реЛрд╣ рд╕реЗ рдХреНрдпрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ:


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

рдЗрд╕ рд▓рдХреНрд╖реНрдп рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛-рдкрд░рд┐рднрд╛рд╖рд┐рдд рдкреНрд░рдХрд╛рд░ рдХреЗ рдЧрд╛рд░реНрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ ред


рдпрд╣реА рд╣реИ, рд╕рдорд╛рд░реЛрд╣ рдШреЛрд╖рдгрд╛ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрдирд╛ рдЪрд╛рд╣рд┐рдП:


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

рдЪрд▓реЛ quartetрдЗрд╕ рддрд░рд╣ рдХреЗ рдПрдХ рд╕рдорд╛рд░реЛрд╣ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ:


import { v } from "quartet";

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

рдЗрд╕ рддрд░рд╣ рдХреЗ рдХреЛрдб рдХреЛ рд▓рд┐рдЦрдиреЗ рдХреЗ рдмрд╛рдж, рд╣рдореЗрдВ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдорд┐рд▓рддрд╛ рд╣реИ рдЬреЛ рдЯрд╛рдЗрдкрдЧрд╛рд░реНрдб рдирд╣реАрдВ рд╣реИ:


chechUser: (value: any) => boolean;

рдЯрд╛рдЗрдкрдЧрд╛рд░реНрдб рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣ рдШреЛрд╖рд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдХрд┐ рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рджреНрд╡рд╛рд░рд╛ рдХрд┐рд╕ рдкреНрд░рдХрд╛рд░ рдХреЛ рдорд╛рдиреНрдп рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдпрд╣ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:


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

рдЖрдЦрд┐рд░рдХрд╛рд░:


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

рдЗрд╕ рдордж рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рджреЛ рдмрд┐рдВрджреБ рд╣реИрдВ:


рдЧрд╛рд░рдВрдЯреА


рдмрд╣реБрдд рддрдереНрдп рдпрд╣ рд╣реИ рдХрд┐ рдбреЗрд╡рд▓рдкрд░ рдпрд╣ рд╕рдВрдХреЗрдд рдХрд░ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рд╕рд░реНрдХрд┐рдЯ рдХрд┐рд╕ рдкреНрд░рдХрд╛рд░ рд╕реЗ рдорд╛рдиреНрдп рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЗрд╕ рддрд░рд╣ рд╕реЗ рд▓рд┐рдЦрдирд╛ рд╕рдВрднрд╡ рд╣реИ:


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

рдЫрд╡рд┐


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

рдЫрд╡рд┐


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

рдЫрд╡рд┐


, 146 .


:


рдЫрд╡рд┐


TypeScript. , , , .



TypeGuard тАФ , .


TypeScript . тАФ .


All Articles