{"id":475791,"date":"2026-04-13T18:16:25","date_gmt":"2026-04-13T18:16:25","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=475791"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=475791","title":{"rendered":"\u041a\u0430\u043a \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0441\u0445\u0435\u043c \u0438 \u0441\u043e\u0437\u0434\u0430\u043b \u0441\u0432\u043e\u044e \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0443 Zod"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043f\u0440\u0438\u0432\u0435\u043b\u0430 \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0441\u0445\u0435\u043c \u0441 runtime-\u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u0435\u0439, \u0430 \u043d\u0430 \u0435\u0451 \u043e\u0441\u043d\u043e\u0432\u0435 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0434\u043b\u044f type-safe \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0444\u043e\u0440\u043c.<\/p>\n<\/blockquote>\n<h3>\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f: \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0431\u0435\u0437 TypeScript<\/h3>\n<p>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043b\u0435\u0442 \u043d\u0430\u0437\u0430\u0434 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043c\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043d\u0430 \u0447\u0438\u0441\u0442\u043e\u043c JavaScript \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0437\u0430\u0434\u0430\u0447\u0430: \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439. \u041e\u0431\u044a\u0435\u043a\u0442\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043b\u0438 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u043e\u0434\u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c \u0441\u0432\u043e\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u0438\u043f\u0430.<\/p>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u043b\u0430\u0441\u044c \u0434\u0432\u0443\u043c\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\u043c\u0438:<\/p>\n<ol>\n<li>\n<p><strong>\u0412\u044b\u0432\u043e\u0434 \u0442\u0438\u043f\u043e\u0432.<\/strong> \u041f\u0440\u043e\u0435\u043a\u0442 \u0431\u044b\u043b \u0431\u0435\u0437 TypeScript, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044e \u0431\u044b\u043b\u0438 JSDoc-\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438. \u041c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0437 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u043b\u0441\u044f \u0442\u0438\u043f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u2014 \u0438 \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e\u0442 \u0442\u0438\u043f \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u043b\u0441\u044f IDE.<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 JSDoc-\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432.<\/strong> \u041a\u043e\u0433\u0434\u0430 \u0438\u0437 \u0441\u0445\u0435\u043c\u044b \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0442\u0438\u043f, \u0432\u0430\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 (\u0442\u0435 \u0441\u0430\u043c\u044b\u0435 JSDoc-\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438) \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u0438\u0441\u044c \u0438 \u0431\u044b\u043b\u0438 \u0432\u0438\u0434\u043d\u044b \u0432 \u0442\u0443\u043b\u0442\u0438\u043f\u0430\u0445 IDE. \u042d\u0442\u043e \u0431\u044b\u043b\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e, \u0433\u0434\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432 \u043a\u043e\u0434\u0435 \u2014 \u043d\u0435\u043f\u043b\u043e\u0445\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u043d\u0438\u0439.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0440\u0435\u043c\u0435\u043d\u0438 \u0431\u044b\u043b\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0438 \u044f \u043d\u0430\u0447\u0430\u043b \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0441\u0432\u043e\u0451. Fluent API, \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b, \u0432\u044b\u0432\u043e\u0434 \u0442\u0438\u043f\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0438 \u2014 \u0432\u0441\u0451 \u043a\u0430\u043a \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043e.<\/p>\n<h3>\u00ab\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u044d\u0442\u043e \u0436\u0435 Zod\u00bb<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 \u044f \u0431\u044b\u043b \u0443\u0436\u0435 \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435, \u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 Zod. \u042f \u043f\u043e\u043c\u043d\u044e \u043c\u043e\u043c\u0435\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u0443\u0432\u0438\u0434\u0435\u043b \u0435\u0433\u043e API \u0438 \u043f\u043e\u0434\u0443\u043c\u0430\u043b: \u00ab\u042d\u0442\u043e \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0442\u043e, \u0447\u0442\u043e \u044f \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0438\u0448\u0443\u00bb. Fluent builder, \u0432\u044b\u0432\u043e\u0434 \u0442\u0438\u043f\u043e\u0432 \u0438\u0437 \u0441\u0445\u0435\u043c\u044b, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u2014 \u0432\u0441\u0451 \u043e\u0434\u0438\u043d \u0432 \u043e\u0434\u0438\u043d.<\/p>\n<p>\u041d\u043e \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0435 \u0431\u0440\u043e\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442. \u041a \u0442\u043e\u043c\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0443 \u043c\u0435\u043d\u044f \u0443\u0436\u0435 \u0431\u044b\u043b\u043e \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439, \u0438 \u044f \u0432\u0438\u0434\u0435\u043b \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0432\u0435\u0449\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u0435\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043d\u0430\u0447\u0435. \u041f\u043b\u044e\u0441, \u043c\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430 \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0438 \u044f \u043c\u043e\u0433 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0430\u0441\u043f\u0435\u043a\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u2014 \u043e\u0442 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f, \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u044e\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0445 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442<\/h3>\n<p>\u041e\u0434\u043d\u0430 \u0438\u0437 \u0432\u0435\u0449\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u0435\u043d\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0430\u043b\u0430 \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0445 \u2014 \u044d\u0442\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0448\u0438\u0431\u043e\u043a \u043f\u043e \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u043c \u043f\u0443\u0442\u044f\u043c:<\/p>\n<pre><code class=\"typescript\">\/\/ \u0422\u0438\u043f\u0438\u0447\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u2014 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044bconst errors = result.getErrors('user.address.city');<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u0442\u0430\u043a? \u0415\u0441\u043b\u0438 \u044f \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u044e <code>address<\/code> \u0432 <code>residenceAddress<\/code>, \u043a\u043e\u0434 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u043d\u043e \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043d\u0443\u0442 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c\u0441\u044f. \u0421\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u043f\u0443\u0442\u0438 \u2014 \u044d\u0442\u043e \u043f\u0443\u0442\u0438 \u043a runtime-\u0431\u0430\u0433\u0430\u043c. \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0432\u044b \u0441\u043a\u0430\u0436\u0435\u0442\u0435 \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 <code>getErrors<\/code> \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u043c-\u0442\u043e \u0432\u0440\u043e\u0434\u0435 <code>'user' | 'user.address' | '<\/code><a href=\"http:\/\/user.address.city\" rel=\"noopener noreferrer nofollow\"><code>user.address.city<\/code><\/a><code>'<\/code>, \u044d\u0442\u043e \u0442\u0430\u043a \u0438 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0440\u0430\u0432\u044b, \u043d\u043e \u043c\u043d\u0435 \u0432 \u0446\u0435\u043b\u043e\u043c \u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0438\u0434\u0435\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441\u043e \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438.<\/p>\n<p>\u041c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0447\u0435\u0433\u043e-\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0433\u043e \u043d\u0430 expression trees \u0438\u0437 C#. \u0423 \u043c\u0435\u043d\u044f \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0431\u044d\u043a\u0433\u0440\u0430\u0443\u043d\u0434 \u0432 C#, \u0438 \u0442\u0430\u043c \u0434\u0430\u0432\u043d\u043e \u0435\u0441\u0442\u044c \u043f\u0430\u0442\u0442\u0435\u0440\u043d, \u043a\u043e\u0433\u0434\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0441\u0442\u0440\u043e\u043a\u0438 \u0432\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0435 \u043b\u044f\u043c\u0431\u0434\u0443, \u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043f\u0443\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442:<\/p>\n<pre><code class=\"typescript\">\/\/ \u0427\u0442\u043e \u044f \u0445\u043e\u0442\u0435\u043b \u2014 type-safe \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044bconst errors = result.getErrorsFor(t =&gt; t.user.address.city);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c <code>t<\/code> \u2014 \u044d\u0442\u043e \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0441\u0445\u0435\u043c\u044b. \u0415\u0441\u043b\u0438 \u044f \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u044e <code>address<\/code>, TypeScript \u0432\u044b\u0434\u0430\u0441\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438. \u0410 IDE \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0430\u0432\u0442\u043e\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432\u043f\u043b\u043e\u0442\u044c \u0434\u043e \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439.<\/p>\n<p>\u0418\u043c\u0435\u043d\u043d\u043e \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0440\u043e\u0441\u043b\u0430 \u043a\u043b\u044e\u0447\u0435\u0432\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u0430\u044f \u0438\u0434\u0435\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u2014 <strong>PropertyDescriptors<\/strong>.<\/p>\n<h3>PropertyDescriptors: \u0441\u0445\u0435\u043c\u0430 \u043a\u0430\u043a \u0434\u0435\u0440\u0435\u0432\u043e \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432<\/h3>\n<p>\u041a\u0430\u0436\u0434\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432 <code>@cleverbrush\/schema<\/code> \u044d\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u0442 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044e-\u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440, \u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432. \u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u044d\u0442\u043e\u0433\u043e \u0434\u0435\u0440\u0435\u0432\u0430 \u0437\u043d\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u041a\u0430\u043a\u043e\u043c\u0443 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0443 \u043e\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u043a \u043d\u0435\u043c\u0443 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u0430<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043f\u0443\u0442\u0438<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u041a\u0442\u043e \u0435\u0433\u043e \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0443\u0437\u0435\u043b<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c type-safe \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0439 \u0433\u043b\u0443\u0431\u0438\u043d\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"typescript\">import { object, string, number, InferType } from '@cleverbrush\/schema';const OrderSchema = object({    id: string(),    customer: object({        name: string().minLength(2),        email: string().email(),        address: object({            city: string().required('\u0413\u043e\u0440\u043e\u0434 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d'),            zip: number()        })    }),    total: number().min(0)});\/\/ InferType \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0442\u0438\u043f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438type Order = InferType&lt;typeof OrderSchema&gt;;\/\/ \u0442.\u043a. \u0432 \u043c\u043e\u0451\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0431\u044b\u043b JS, \u0442\u043e \u044f \u0434\u0435\u043b\u0430\u043b \u0442\u0430\u043a\/\/ \/** @type {InferType&lt;typeof OrderSchema&gt;} *\/\/\/ const order = ....const result = OrderSchema.validate({    id: '123',    customer: {        name: 'A', \/\/ \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435        email: 'not-an-email',        address: {            \/\/ city \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442            zip: -1        }    },    total: -5});if (!result.valid) {    \/\/ Type-safe \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b \u2014 IDE \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043f\u043e\u043b\u044f    const nameErrors = result.getErrorsFor(t =&gt; t.customer.name);    \/\/ { isValid: false, errors: ['\u043c\u0438\u043d\u0438\u043c\u0443\u043c 2 \u0441\u0438\u043c\u0432\u043e\u043b\u0430'], seenValue: 'A' }    const cityErrors = result.getErrorsFor(t =&gt; t.customer.address.city);    \/\/ { isValid: false, errors: ['\u0413\u043e\u0440\u043e\u0434 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d'], seenValue: undefined }    \/\/ \u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0435\u0442\u0435 address \u2192 residenceAddress?    \/\/ TypeScript \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0437\u0434\u0435\u0441\u044c \u2191}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u0418\u043c\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 fluent API<\/h3>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u043c\u0435\u0442\u043e\u0434\u0430 \u043d\u0430 \u0441\u0445\u0435\u043c\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <strong>\u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440<\/strong> \u0431\u0438\u043b\u0434\u0435\u0440\u0430. \u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043c\u0443\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f:<\/p>\n<pre><code class=\"typescript\">const base = string().minLength(2);const withMax = base.maxLength(100);    \/\/ \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440const optional = base.optional();        \/\/ \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\/\/ base, withMax \u0438 optional \u2014 \u0442\u0440\u0438 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u0441\u0445\u0435\u043c\u044b<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u044b, \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u043a\u0430\u043a \u00ab\u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0431\u043b\u043e\u043a\u0438\u00bb \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u043c\u043e\u0434\u0443\u043b\u044f\u043c\u0438 \u0431\u0435\u0437 \u0441\u0442\u0440\u0430\u0445\u0430 \u043f\u043e\u0431\u043e\u0447\u043d\u044b\u0445 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432.<\/p>\n<h3>\u041a\u0430\u043a\u0438\u0435 \u0431\u0438\u043b\u0434\u0435\u0440\u044b \u0435\u0441\u0442\u044c<\/h3>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 14 \u0442\u0438\u043f\u043e\u0432 \u0431\u0438\u043b\u0434\u0435\u0440\u043e\u0432:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0411\u0438\u043b\u0434\u0435\u0440<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u041f\u0440\u0438\u043c\u0435\u0440<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>string()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0421\u0442\u0440\u043e\u043a\u0438 \u0441 <code>minLength<\/code>, <code>maxLength<\/code>, <code>matches<\/code>, <code>email<\/code>, <code>url<\/code>, <code>uuid<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>string().email().minLength(5)<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>number()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0427\u0438\u0441\u043b\u0430 \u0441 <code>min<\/code>, <code>max<\/code>, <code>positive<\/code>, <code>negative<\/code>, <code>finite<\/code>, <code>multipleOf<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>number().min(0).max(100)<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>boolean()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0411\u0443\u043b\u0435\u0432\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>boolean().required()<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>date()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0414\u0430\u0442\u044b \u0441 <code>minDate<\/code>, <code>maxDate<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>date().minDate(new Date())<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>object()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041e\u0431\u044a\u0435\u043a\u0442\u044b \u0441 \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>object({ name: string() })<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>array()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041c\u0430\u0441\u0441\u0438\u0432\u044b \u0441 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>array(string()).nonempty()<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>union()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u0432, \u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0438\u0440\u0443\u0435\u043c\u044b\u0435<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>union(string()).or(number())<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>tuple()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u043e\u0440\u0442\u0435\u0436\u0438 \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0434\u043b\u0438\u043d\u044b<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>tuple([string(), number()])<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>record()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0417\u0430\u043f\u0438\u0441\u0438 \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u043a\u043b\u044e\u0447\u0430\u043c\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>record(string(), number())<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>func()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0435\u0439<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>func()<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>any()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>any().hasType&lt;Map&gt;()<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>nul()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>null<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>nul()<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>lazy()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041b\u0435\u043d\u0438\u0432\u044b\u0435 (\u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0435) \u0441\u0445\u0435\u043c\u044b<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>lazy(() =&gt; TreeSchema)<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>extern()<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041e\u0431\u0451\u0440\u0442\u043a\u0430 \u0447\u0443\u0436\u0438\u0445 Standard Schema v1 \u0441\u0445\u0435\u043c<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>extern(zodSchema)<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0431\u0438\u043b\u0434\u0435\u0440 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e <code>SchemaBuilder<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0431\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b:<\/p>\n<pre><code class=\"typescript\">schema    .optional()                    \/\/ \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 undefined    .required('\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435') \/\/ \u043d\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 undefined    .nullable()                    \/\/ \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 null    .default('fallback')           \/\/ \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043f\u0440\u0438 undefined    .catch('safe')                 \/\/ \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043b\u044e\u0431\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438    .readonly()                    \/\/ \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0442\u0438\u043f\u043e\u0432: Readonly&lt;T&gt;    .brand&lt;'UserId'&gt;()             \/\/ branded type    .describe('\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044f')     \/\/ \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 (\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0441\u0445\u0435\u043c\u0435 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u044e)    .addValidator(fn)              \/\/ \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440    .addPreprocessor(fn)           \/\/ \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u0434 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0435\u0439    .introspect()                  \/\/ \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0445\u0435\u043c\u044b<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>Runtime-\u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u044f: .introspect()<\/h3>\n<p>\u041a\u0430\u0436\u0434\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432 <code>@cleverbrush\/schema<\/code> \u2014 \u044d\u0442\u043e \u043d\u0435 \u0447\u0451\u0440\u043d\u044b\u0439 \u044f\u0449\u0438\u043a. \u041c\u0435\u0442\u043e\u0434 <code>.introspect()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0445\u0435\u043c\u044b \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430: \u0442\u0438\u043f, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u0444\u043b\u0430\u0433\u0438, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f, \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439. \u0412\u0441\u0451, \u0447\u0442\u043e \u0432\u044b \u0437\u0430\u0434\u0430\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 fluent API, \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435:<\/p>\n<pre><code class=\"typescript\">import { string, number, object } from '@cleverbrush\/schema';const Name = string()    .minLength(2)    .maxLength(100)    .describe('\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f')    .optional();const info = Name.introspect();info.type;          \/\/ 'string'info.isRequired;    \/\/ false (\u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e .optional())info.isNullable;    \/\/ falseinfo.minLength;     \/\/ 2info.maxLength;     \/\/ 100info.description;   \/\/ '\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f'info.extensions;    \/\/ {}const Age = number().min(18).default(25);const ageInfo = Age.introspect();ageInfo.type;         \/\/ 'number'ageInfo.hasDefault;   \/\/ trueageInfo.defaultValue; \/\/ 25<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0442\u0438\u043f \u0431\u0438\u043b\u0434\u0435\u0440\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 <code>introspect()<\/code> \u0441\u0432\u043e\u0438\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438: <code>StringSchemaBuilder<\/code> \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 <code>minLength<\/code>, <code>maxLength<\/code>, <code>NumberSchemaBuilder<\/code> \u2014 <code>min<\/code>, <code>max<\/code> \u0438 \u0442.\u0434.<\/p>\n<p>\u0417\u0430\u0447\u0435\u043c \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e? \u0418\u043c\u0435\u043d\u043d\u043e \u043d\u0430 <code>introspect()<\/code> \u043e\u043f\u0438\u0440\u0430\u0435\u0442\u0441\u044f <code>@cleverbrush\/schema-json<\/code> \u0434\u043b\u044f \u0434\u0432\u0443\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u0432 JSON Schema, <code>@cleverbrush\/react-form<\/code> \u0434\u043b\u044f \u0432\u044b\u0431\u043e\u0440\u0430 \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440\u0430 \u043f\u043e \u0442\u0438\u043f\u0443 \u043f\u043e\u043b\u044f, \u0438 \u043b\u044e\u0431\u043e\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u00ab\u0437\u0430\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u044c\u00bb \u0441\u0445\u0435\u043c\u044b.<\/p>\n<h3>\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439<\/h3>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0433\u0438\u0433\u0430\u043d\u0442\u0441\u043a\u0438\u0439 \u0431\u0438\u043b\u0434\u0435\u0440 \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u0430\u043c\u0438, \u044f\u0434\u0440\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u0412\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u2014 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u2014 \u044d\u0442\u043e \u043d\u0435 \u00ab\u0447\u0451\u0440\u043d\u044b\u0435 \u044f\u0449\u0438\u043a\u0438\u00bb \u0432\u0440\u043e\u0434\u0435 <code>.refine()<\/code> \u0432 Zod. \u041e\u043d\u0438 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u044b, \u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0442\u0438\u0440\u0443\u0435\u043c\u044b \u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0443\u0435\u043c\u044b:<\/p>\n<pre><code class=\"typescript\">import { defineExtension, withExtensions, StringSchemaBuilder, NumberSchemaBuilder } from '@cleverbrush\/schema';\/\/ \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u0442\u0440\u043e\u043a \u2014 HEX-\u0446\u0432\u0435\u0442const hexColorExt = defineExtension({    string: {        hexColor(this: StringSchemaBuilder) {            return this.addValidator((v) =&gt; {                const valid = \/^#[0-9a-f]{6}$\/i.test(v as string);                return {                    valid,                    errors: valid ? [] : [{ message: '\u0414\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c HEX-\u0446\u0432\u0435\u0442' }]                };            });        }    }});\/\/ \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0447\u0438\u0441\u0435\u043b \u2014 \u043f\u043e\u0440\u0442const portExt = defineExtension({    number: {        port(this: NumberSchemaBuilder) {            return this.isInteger().min(1).max(65535);        }    }});\/\/ \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0431\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0441\u0440\u0430\u0437\u0443 \u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0431\u0440\u0438\u043a\u0438const s = withExtensions(hexColorExt, portExt);\/\/ .hexColor() \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430\u0445, .port() \u2014 \u043d\u0430 \u0447\u0438\u0441\u043b\u0430\u0445const colorSchema = s.string().hexColor();const portSchema = s.number().port();\/\/ \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u043d\u044b \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u044ecolorSchema.introspect().extensions; \/\/ { hexColor: true }portSchema.introspect().extensions;  \/\/ { port: true }\/\/ \u041c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u043d\u044b\u0445 \u0441\u0445\u0435\u043c\u0430\u0445const ServerConfig = s.object({    themeColor: s.string().hexColor(),    port: s.number().port(),    name: s.string().minLength(1)});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f (<code>email<\/code>, <code>url<\/code>, <code>uuid<\/code>, <code>positive<\/code>, <code>nonempty<\/code>, <code>oneOf<\/code> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435) \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0442\u0435\u043c \u0436\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u043c \u2014 \u043e\u043d\u0438 \u043d\u0435 \u0432\u0445\u043e\u0434\u044f\u0442 \u0432 \u044f\u0434\u0440\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<h3>Standard Schema v1: \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c \u0441 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439<\/h3>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 <a href=\"https:\/\/standardschema.dev\/\" rel=\"noopener noreferrer nofollow\">Standard Schema v1<\/a>. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e <code>@cleverbrush\/schema<\/code> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0441 <strong>tRPC<\/strong>, <strong>TanStack Form<\/strong>, <strong>React Hook Form<\/strong>, <strong>T3 Env<\/strong>, <strong>Hono<\/strong> \u0438 50+ \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u2014 \u0432\u0435\u0437\u0434\u0435, \u0433\u0434\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f Standard Schema:<\/p>\n<pre><code class=\"typescript\">const UserSchema = object({    name: string().minLength(2),    email: string().email()});\/\/ \u041f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0432 tRPC, TanStack Form \u0438 \u0442.\u0434.const standardSchema = UserSchema['~standard'];<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410 <code>extern()<\/code> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u2014 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0447\u0443\u0436\u0438\u0435 Standard Schema \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 (Zod, Valibot, ArkType) \u0432 \u0431\u0438\u043b\u0434\u0435\u0440 <code>@cleverbrush\/schema<\/code>:<\/p>\n<pre><code class=\"typescript\">import { z } from 'zod';import { extern, object, string } from '@cleverbrush\/schema';\/\/ \u041e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c Zod-\u0441\u0445\u0435\u043c\u0443const zodAddress = z.object({    city: z.string(),    zip: z.number()});\/\/ \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u043c\u0438 \u0441\u0445\u0435\u043c\u0430\u043c\u0438const UserSchema = object({    name: string(),    address: extern(zodAddress)});\/\/ getErrorsFor \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u0430\u0436\u0435 \u0434\u043b\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432 Zod-\u0441\u0445\u0435\u043c\u044bconst result = UserSchema.validate({ name: 'Alice', address: {} });const cityErrors = result.getErrorsFor(t =&gt; t.address.city);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 PropertyDescriptors: \u043c\u0430\u043f\u043f\u0435\u0440<\/h3>\n<p>PropertyDescriptors \u2014 \u044d\u0442\u043e \u043d\u0435 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u0430\u044f \u0444\u0438\u0447\u0430. \u041e\u043d\u0438 \u043b\u0435\u0436\u0430\u0442 \u0432 \u043e\u0441\u043d\u043e\u0432\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u041f\u0435\u0440\u0432\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u2014 <code>@cleverbrush\/mapper<\/code>.<\/p>\n<p>\u0412 .NET \u0435\u0441\u0442\u044c AutoMapper \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u0430\u043f\u043f\u0438\u0442 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439, \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u043e \u0438\u043c\u0435\u043d\u0438. \u041c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0447\u0435\u0433\u043e-\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e, \u043d\u043e \u0434\u043b\u044f \u0441\u0445\u0435\u043c: \u0447\u0442\u043e\u0431\u044b \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u0431\u044b\u043b type-safe, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u043b \u043f\u043e\u043b\u043d\u043e\u0442\u0443, \u0438 \u0447\u0442\u043e\u0431\u044b \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0431\u044b\u043b\u0438 \u0442\u0435\u043c\u0438 \u0436\u0435 PropertyDescriptors. \u0422\u0430\u043a \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f <code>@cleverbrush\/mapper<\/code>.<\/p>\n<p>\u0421 \u043c\u0430\u043f\u043f\u0435\u0440\u043e\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 type-safe:<\/p>\n<pre><code class=\"typescript\">import { object, string, number } from '@cleverbrush\/schema';import { mapper } from '@cleverbrush\/mapper';\/\/ --- \u0421\u0445\u0435\u043c\u044b \u0430\u0434\u0440\u0435\u0441\u043e\u0432 ---const ApiAddress = object({    city: string(),    house_nr: number()});const DomainAddress = object({    city: string(),    houseNr: number()});\/\/ --- \u0421\u0445\u0435\u043c\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 ---const ApiUser = object({    id: string(),    first_name: string(),    last_name: string(),    birth_year: number(),    address: ApiAddress});const DomainUser = object({    id: string(),    fullName: string(),    age: number(),    address: DomainAddress});const registry = mapper()    \/\/ \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u0430\u0434\u0440\u0435\u0441\u043e\u0432    .configure(ApiAddress, DomainAddress, m =&gt;        m            \/\/ city \u2192 city \u2014 \u0430\u0432\u0442\u043e\u043c\u0430\u043f\u043f\u0438\u043d\u0433 (\u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e\u0435 \u0438\u043c\u044f \u0438 \u0442\u0438\u043f)            .for(t =&gt; t.houseNr).from(f =&gt; f.house_nr)    )    \/\/ \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439    .configure(ApiUser, DomainUser, m =&gt;        m            \/\/ id \u2192 id \u2014 \u0430\u0432\u0442\u043e\u043c\u0430\u043f\u043f\u0438\u043d\u0433 (\u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e\u0435 \u0438\u043c\u044f \u0438 \u0442\u0438\u043f)            \/\/ address \u2192 address \u2014 \u0430\u0432\u0442\u043e\u043c\u0430\u043f\u043f\u0438\u043d\u0433 (\u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d \u0432\u044b\u0448\u0435!)            .for(t =&gt; t.fullName)                .compute(src =&gt; `${src.first_name} ${src.last_name}`)            .for(t =&gt; t.age)                .compute(src =&gt; new Date().getFullYear() - src.birth_year)    );const mapFn = registry.getMapper(ApiUser, DomainUser);const user = await mapFn({    id: 'u-42',    first_name: '\u0418\u0432\u0430\u043d',    last_name: '\u041f\u0435\u0442\u0440\u043e\u0432',    birth_year: 1990,    address: { city: '\u041c\u043e\u0441\u043a\u0432\u0430', house_nr: 15 }});\/\/ { id: 'u-42', fullName: '\u0418\u0432\u0430\u043d \u041f\u0435\u0442\u0440\u043e\u0432', age: 36, address: { city: '\u041c\u043e\u0441\u043a\u0432\u0430', houseNr: 15 } }<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u0432\u0430\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\n<p><strong>\u0410\u0432\u0442\u043e\u043c\u0430\u043f\u043f\u0438\u043d\u0433<\/strong>: \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0441 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u043c \u0438\u043c\u0435\u043d\u0435\u043c \u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u043c \u0442\u0438\u043f\u043e\u043c \u043c\u0430\u043f\u043f\u044f\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u2014 \u0432\u044b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u0435\u0442\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u043e, \u0447\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>Compile-time \u043f\u043e\u043b\u043d\u043e\u0442\u0430<\/strong>: \u0435\u0441\u043b\u0438 \u0432\u044b \u0437\u0430\u0431\u044b\u043b\u0438 \u0437\u0430\u043c\u0430\u043f\u0438\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0441\u0445\u0435\u043c\u044b, TypeScript \u0432\u044b\u0434\u0430\u0441\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 <strong>\u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438<\/strong>.<\/p>\n<\/li>\n<li>\n<p><strong>Type-safe \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b<\/strong>: \u0438 <code>.for()<\/code>, \u0438 <code>.from()<\/code> \u2014 \u044d\u0442\u043e \u0442\u0435 \u0436\u0435 PropertyDescriptor-\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b. \u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043b\u0438 \u043f\u043e\u043b\u0435? TypeScript \u043f\u043e\u043a\u0430\u0436\u0435\u0442.<\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0435 \u0442\u0438\u043f\u044b<\/strong>: \u0435\u0441\u043b\u0438 \u0432\u044b \u043f\u044b\u0442\u0430\u0435\u0442\u0435\u0441\u044c <code>.from()<\/code> \u0447\u0438\u0441\u043b\u043e \u0432 \u0441\u0442\u0440\u043e\u043a\u0443, \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0440\u0443\u0433\u043d\u0451\u0442\u0441\u044f \u0438 \u0432\u044b \u043f\u043e\u0439\u043c\u0451\u0442\u0435 \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 <code>.compute()<\/code>.<\/p>\n<\/li>\n<\/ul>\n<h3>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 PropertyDescriptors: React-\u0444\u043e\u0440\u043c\u044b<\/h3>\n<p>\u0412\u0442\u043e\u0440\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u2014 <code>@cleverbrush\/react-form<\/code>. Headless \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u0432 React.<\/p>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432\u0441\u0435\u0445 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 React-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0434\u043b\u044f \u0444\u043e\u0440\u043c (\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f \u0432\u0438\u0434\u0435\u043b) \u2014 React Hook Form, Formik, React Final Form \u2014 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043f\u043e\u043b\u044f \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u043c \u0438\u043c\u0435\u043d\u0430\u043c: <code>register(\"email\")<\/code>, <code>&lt;Field name=\"<\/code><a href=\"http:\/\/address.city\" rel=\"noopener noreferrer nofollow\"><code>address.city<\/code><\/a><code>\" \/&gt;<\/code>. \u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043b\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u2014 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043c\u043e\u043b\u0447\u0438\u0442, \u0444\u043e\u0440\u043c\u0430 \u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435.<\/p>\n<p><code>@cleverbrush\/react-form<\/code> \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u044f \u0447\u0435\u0440\u0435\u0437 \u0442\u0435 \u0436\u0435 PropertyDescriptor-\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b: <code>(t) =&gt; <\/code><a href=\"http:\/\/t.address.city\" rel=\"noopener noreferrer nofollow\"><code>t.address.city<\/code><\/a>. \u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043b\u0438 \u2014 \u043e\u0448\u0438\u0431\u043a\u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438. \u041e\u043f\u0435\u0447\u0430\u0442\u0430\u043b\u0438\u0441\u044c \u2014 \u043e\u0448\u0438\u0431\u043a\u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438. \u041f\u043b\u044e\u0441, \u0441\u0445\u0435\u043c\u0430 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438 \u0442\u0438\u043f\u043e\u043c, \u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0435\u0439, \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u043f\u043e\u043b\u0435\u0439 \u0444\u043e\u0440\u043c\u044b.<\/p>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u2014 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440\u043e\u0432. \u0412\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0435 \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440\u044b \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0441\u0445\u0435\u043c\u044b \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440. \u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>Field<\/code> \u0441\u0430\u043c \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u043d\u0443\u0436\u043d\u044b\u0439 \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440 \u043f\u043e \u0442\u0438\u043f\u0443 \u0441\u0445\u0435\u043c\u044b \u043f\u043e\u043b\u044f:<\/p>\n<pre><code>import { object, string, number } from '@cleverbrush\/schema';import { useSchemaForm, FormSystemProvider, Field } from '@cleverbrush\/react-form';\/\/ \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440\u044b \u043e\u0434\u0438\u043d \u0440\u0430\u0437const renderers = {    string: ({ value, onChange, onBlur, error, touched, label }) =&gt; (        &lt;div&gt;            &lt;label&gt;{label}&lt;\/label&gt;            &lt;input                value={value ?? ''}                onChange={e =&gt; onChange(e.target.value)}                onBlur={onBlur}            \/&gt;            {touched &amp;&amp; error &amp;&amp; &lt;span className=\"error\"&gt;{error}&lt;\/span&gt;}        &lt;\/div&gt;    ),    number: ({ value, onChange, onBlur, error, touched, label }) =&gt; (        &lt;div&gt;            &lt;label&gt;{label}&lt;\/label&gt;            &lt;input                type=\"number\"                value={value ?? ''}                onChange={e =&gt; onChange(Number(e.target.value))}                onBlur={onBlur}            \/&gt;            {touched &amp;&amp; error &amp;&amp; &lt;span className=\"error\"&gt;{error}&lt;\/span&gt;}        &lt;\/div&gt;    )};\/\/ \u0421\u0445\u0435\u043c\u0430 \u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0438\u0441\u0442\u0438\u043d\u044bconst ContactSchema = object({    name: string().required('\u0418\u043c\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e').minLength(2),    email: string().required('Email \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d').email(),    age: number().min(18, '\u041c\u0438\u043d\u0438\u043c\u0443\u043c 18 \u043b\u0435\u0442')});function ContactForm() {    const form = useSchemaForm(ContactSchema);    const handleSubmit = async () =&gt; {        const result = await form.submit();        if (result.valid) {            console.log('\u0414\u0430\u043d\u043d\u044b\u0435:', result.object);        }    };    return (        &lt;FormSystemProvider renderers={renderers}&gt;            {\/* Type-safe \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u2014 IDE \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u044f *\/}            &lt;Field forProperty={t =&gt; t.name} form={form} label=\"\u0418\u043c\u044f\" \/&gt;            &lt;Field forProperty={t =&gt; t.email} form={form} label=\"Email\" \/&gt;            &lt;Field forProperty={t =&gt; t.age} form={form} label=\"\u0412\u043e\u0437\u0440\u0430\u0441\u0442\" \/&gt;            &lt;button onClick={handleSubmit}&gt;\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c&lt;\/button&gt;        &lt;\/FormSystemProvider&gt;    );}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 headless \u2014 \u043e\u043d\u0430 \u043d\u0435 \u043d\u0430\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 UI. \u0425\u043e\u0442\u0438\u0442\u0435 Material UI? \u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440\u044b. \u0425\u043e\u0442\u0438\u0442\u0435 Ant Design? \u0422\u043e\u0436\u0435. \u041c\u043e\u0436\u043d\u043e \u0432\u043b\u043e\u0436\u0438\u0442\u044c <code>FormSystemProvider<\/code> \u0434\u0440\u0443\u0433 \u0432 \u0434\u0440\u0443\u0433\u0430, \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440\u044b \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0435\u043a\u0446\u0438\u0439.<\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u043d\u043a\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0435\u0441\u0442\u044c <code>useField<\/code>:<\/p>\n<pre><code>function CustomNameField() {    const form = useSchemaForm(ContactSchema);    const field = form.useField(t =&gt; t.name);    return (        &lt;div&gt;            &lt;input                value={field.value ?? ''}                onChange={e =&gt; field.onChange(e.target.value)}                onBlur={field.onBlur}            \/&gt;            &lt;p&gt;Dirty: {String(field.dirty)}&lt;\/p&gt;            &lt;p&gt;Touched: {String(field.touched)}&lt;\/p&gt;            {field.error &amp;&amp; &lt;p className=\"error\"&gt;{field.error}&lt;\/p&gt;}        &lt;\/div&gt;    );}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u0422\u0438\u043f\u044b \u0438 \u0442\u0435\u0441\u0442\u044b \u0442\u0438\u043f\u043e\u0432<\/h3>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u2014 \u044d\u0442\u043e \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044f. \u041e\u0434\u043d\u0438 \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043d\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043b\u043e\u0441\u044c \u0440\u0435\u0448\u0430\u0442\u044c \u0432 TypeScript, \u0431\u044b\u043b\u0438 \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 mapped types, conditional types \u0438 \u0432\u044b\u0432\u043e\u0434\u043e\u043c \u0442\u0438\u043f\u043e\u0432 \u0438\u0437 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u0441\u0445\u0435\u043c.<\/p>\n<p>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432:<\/p>\n<ul>\n<li>\n<p><code>InferType&lt;typeof schema&gt;<\/code> \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043f\u043e\u043b\u043d\u044b\u0439 TypeScript-\u0442\u0438\u043f \u0438\u0437 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u044b, \u0432\u043a\u043b\u044e\u0447\u0430\u044f <code>optional<\/code>, <code>nullable<\/code>, <code>readonly<\/code> \u0438 \u0431\u0440\u0435\u043d\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0442\u0438\u043f\u044b.<\/p>\n<\/li>\n<li>\n<p>PropertyDescriptor tree \u2014 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0439 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0442\u0440\u043e\u0438\u0442 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0439 \u0433\u043b\u0443\u0431\u0438\u043d\u044b.<\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u043f\u043f\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 conditional types, \u0447\u0442\u043e\u0431\u044b \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0441\u0445\u0435\u043c\u044b \u0437\u0430\u043c\u0430\u043f\u043b\u0435\u043d\u044b.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u0432 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>expectTypeOf<\/code> \u0438\u0437 Vitest \u2014 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0442\u0438\u043f\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u043e\u043c\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440:<\/p>\n<pre><code class=\"typescript\">import { expectTypeOf } from 'vitest';const schema = object({    name: string(),    age: number().optional()});type T = InferType&lt;typeof schema&gt;;expectTypeOf&lt;T&gt;().toEqualTypeOf&lt;{    name: string;    age?: number;}&gt;();<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432\u0435\u0441\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u0434 \u043f\u043e\u043a\u0440\u044b\u0442 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u043b\u043e\u0433\u0438\u043a\u0438, \u0430 \u0442\u0435\u0441\u0442\u044b \u0442\u0438\u043f\u043e\u0432 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044e\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u043e\u0432 \u0442\u0438\u043f\u043e\u0432 \u043f\u0440\u0438 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0435. \u0422\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043f\u043e \u0432\u0441\u0435\u043c\u0443 \u043c\u043e\u043d\u043e\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e \u2014 <strong>97.9%<\/strong>.<\/p>\n<h3>\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/h3>\n<p>\u0420\u0430\u0437 \u0443\u0436 \u043c\u044b \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0441 Zod \u2014 \u0432\u043e\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0431\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u043e\u0432 (Vitest bench, Zod v4, \u043e\u0434\u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u0430):<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0411\u0435\u043d\u0447\u043c\u0430\u0440\u043a<\/p>\n<\/th>\n<th>\n<p align=\"left\"><a href=\"\/users\/cleverbrush\" rel=\"noopener noreferrer nofollow\">@cleverbrush<\/a>\/schema<\/p>\n<\/th>\n<th>\n<p align=\"left\">Zod v4<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u0420\u0430\u0437\u043d\u0438\u0446\u0430<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041c\u0430\u0441\u0441\u0438\u0432 100 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 (valid)<\/p>\n<\/td>\n<td>\n<p align=\"left\">35,228 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">13,277 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>2.65\u00d7 \u0431\u044b\u0441\u0442\u0440\u0435\u0435<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041c\u0430\u0441\u0441\u0438\u0432 100 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 (invalid)<\/p>\n<\/td>\n<td>\n<p align=\"left\">899,329 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">4,396 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>204\u00d7 \u0431\u044b\u0441\u0442\u0440\u0435\u0435<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0421\u043b\u043e\u0436\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 &#8212; \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0440\u043e\u0432\u043d\u0435\u0439 (valid)<\/p>\n<\/td>\n<td>\n<p align=\"left\">198,988 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">136,090 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>1.46\u00d7 \u0431\u044b\u0441\u0442\u0440\u0435\u0435<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0412\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 (valid)<\/p>\n<\/td>\n<td>\n<p align=\"left\">690,556 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">368,893 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>1.87\u00d7 \u0431\u044b\u0441\u0442\u0440\u0435\u0435<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0412\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 (invalid)<\/p>\n<\/td>\n<td>\n<p align=\"left\">2,739,319 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">87,245 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>31.4\u00d7 \u0431\u044b\u0441\u0442\u0440\u0435\u0435<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Union (\u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0432\u0435\u0442\u043a\u0430)<\/p>\n<\/td>\n<td>\n<p align=\"left\">676,107 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">732,682 ops\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">Zod ~8% \u0431\u044b\u0441\u0442\u0440\u0435\u0435<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0431\u0435\u043d\u0447\u043c\u0430\u0440\u043a, \u0433\u0434\u0435 Zod \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u2014 union match \u043f\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0432\u0435\u0442\u043a\u0435 (~8%). \u0421\u0447\u0438\u0442\u0430\u044e \u044d\u0442\u043e \u043f\u0430\u0440\u0438\u0442\u0435\u0442\u043e\u043c.<\/p>\n<h3>\u0420\u0430\u0437\u043c\u0435\u0440 \u0431\u0430\u043d\u0434\u043b\u0430<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0411\u0430\u043d\u0434\u043b<\/p>\n<\/th>\n<th>\n<p align=\"left\">Gzipped<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>@cleverbrush\/schema<\/code> (\u043f\u043e\u043b\u043d\u044b\u0439)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>14 KB<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>@cleverbrush\/schema\/string<\/code> (subpath)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>3.8 KB<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Zod v3 (\u043f\u043e\u043b\u043d\u044b\u0439)<\/p>\n<\/td>\n<td>\n<p align=\"left\">14.4 KB<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Zod v4 (\u043f\u043e\u043b\u043d\u044b\u0439)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>41 KB<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0412 3 \u0440\u0430\u0437\u0430 \u043c\u0435\u043d\u044c\u0448\u0435 Zod v4. Sub-path \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u044b (<code>\/string<\/code>, <code>\/number<\/code>, <code>\/object<\/code> \u0438 \u0442.\u0434.) \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0443\u0436\u043d\u044b\u0435 \u0431\u0438\u043b\u0434\u0435\u0440\u044b.<\/p>\n<h3>\u041d\u043e\u043b\u044c runtime-\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439<\/h3>\n<p>\u0412\u0441\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u043c\u043e\u043d\u043e\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u2014 zero runtime dependencies. Strict TypeScript, Biome \u0434\u043b\u044f \u043b\u0438\u043d\u0442\u0438\u043d\u0433\u0430, Vitest \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432, tsup \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438.<\/p>\n<h3>\u0418\u0442\u043e\u0433\u0438<\/h3>\n<p>\u042f \u043d\u0435 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044e, \u0447\u0442\u043e \u044d\u0442\u043e \u00ab\u0443\u0431\u0438\u0439\u0446\u0430 Zod\u00bb. Zod \u2014 \u043e\u0442\u043b\u0438\u0447\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0441 \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439. \u041d\u043e \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u0432\u0430\u0436\u043d\u044b runtime-\u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u044f \u0441\u0445\u0435\u043c, type-safe \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0448\u0438\u0431\u043e\u043a \u0431\u0435\u0437 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0445 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442 \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e, \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u0438 \u0444\u043e\u0440\u043c\u044b \u2014 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 <code>@cleverbrush\/schema<\/code>.<\/p>\n<h3>\u0421\u0441\u044b\u043b\u043a\u0438<\/h3>\n<ul>\n<li>\n<p><strong>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0438 playground:<\/strong> <a href=\"http:\/\/docs.cleverbrush.com\" rel=\"noopener noreferrer nofollow\">docs.cleverbrush.com<\/a><\/p>\n<\/li>\n<li>\n<p><strong>GitHub:<\/strong> <a href=\"https:\/\/github.com\/cleverbrush\/framework?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=launch\" rel=\"noopener noreferrer nofollow\">github.com\/cleverbrush\/framework<\/a><\/p>\n<\/li>\n<li>\n<p><strong>npm:<\/strong> <code>npm install @cleverbrush\/schema<\/code><\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u043b\u044e\u0431\u043e\u0439 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438 \u2014 \u043f\u043e API, \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u043c \u0444\u0438\u0447\u0430\u043c. Issues \u0438 PR \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442\u0441\u044f.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1023038\/\">https:\/\/habr.com\/ru\/articles\/1023038\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043f\u0440\u0438\u0432\u0435\u043b\u0430 \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0441\u0445\u0435\u043c \u0441 runtime-\u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u0435\u0439, \u0430 \u043d\u0430 \u0435\u0451 \u043e\u0441\u043d\u043e\u0432\u0435 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0434\u043b\u044f type-safe \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0444\u043e\u0440\u043c.\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f: \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0431\u0435\u0437 TypeScript\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043b\u0435\u0442 \u043d\u0430\u0437\u0430\u0434 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043c\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043d\u0430 \u0447\u0438\u0441\u0442\u043e\u043c JavaScript \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0437\u0430\u0434\u0430\u0447\u0430: \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439. \u041e\u0431\u044a\u0435\u043a\u0442\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043b\u0438 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u043e\u0434\u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c \u0441\u0432\u043e\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u0438\u043f\u0430.\u0417\u0430\u0434\u0430\u0447\u0430 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u043b\u0430\u0441\u044c \u0434\u0432\u0443\u043c\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\u043c\u0438:\u0412\u044b\u0432\u043e\u0434 \u0442\u0438\u043f\u043e\u0432. \u041f\u0440\u043e\u0435\u043a\u0442 \u0431\u044b\u043b \u0431\u0435\u0437 TypeScript, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044e \u0431\u044b\u043b\u0438 JSDoc-\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438. \u041c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0437 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u043b\u0441\u044f \u0442\u0438\u043f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u2014 \u0438 \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e\u0442 \u0442\u0438\u043f \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u043b\u0441\u044f IDE.\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 JSDoc-\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432. \u041a\u043e\u0433\u0434\u0430 \u0438\u0437 \u0441\u0445\u0435\u043c\u044b \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0442\u0438\u043f, \u0432\u0430\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 (\u0442\u0435 \u0441\u0430\u043c\u044b\u0435 JSDoc-\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438) \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u0438\u0441\u044c \u0438 \u0431\u044b\u043b\u0438 \u0432\u0438\u0434\u043d\u044b \u0432 \u0442\u0443\u043b\u0442\u0438\u043f\u0430\u0445 IDE. \u042d\u0442\u043e \u0431\u044b\u043b\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e, \u0433\u0434\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432 \u043a\u043e\u0434\u0435 \u2014 \u043d\u0435\u043f\u043b\u043e\u0445\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u043d\u0438\u0439.\u0412\u0440\u0435\u043c\u0435\u043d\u0438 \u0431\u044b\u043b\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0438 \u044f \u043d\u0430\u0447\u0430\u043b \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0441\u0432\u043e\u0451. Fluent API, \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b, \u0432\u044b\u0432\u043e\u0434 \u0442\u0438\u043f\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0438 \u2014 \u0432\u0441\u0451 \u043a\u0430\u043a \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043e.\u00ab\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u044d\u0442\u043e \u0436\u0435 Zod\u00bb\u041a\u043e\u0433\u0434\u0430 \u044f \u0431\u044b\u043b \u0443\u0436\u0435 \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435, \u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 Zod. \u042f \u043f\u043e\u043c\u043d\u044e \u043c\u043e\u043c\u0435\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u0443\u0432\u0438\u0434\u0435\u043b \u0435\u0433\u043e API \u0438 \u043f\u043e\u0434\u0443\u043c\u0430\u043b: \u00ab\u042d\u0442\u043e \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0442\u043e, \u0447\u0442\u043e \u044f \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0438\u0448\u0443\u00bb. Fluent builder, \u0432\u044b\u0432\u043e\u0434 \u0442\u0438\u043f\u043e\u0432 \u0438\u0437 \u0441\u0445\u0435\u043c\u044b, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u2014 \u0432\u0441\u0451 \u043e\u0434\u0438\u043d \u0432 \u043e\u0434\u0438\u043d.\u041d\u043e \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0435 \u0431\u0440\u043e\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442. \u041a \u0442\u043e\u043c\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0443 \u043c\u0435\u043d\u044f \u0443\u0436\u0435 \u0431\u044b\u043b\u043e \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439, \u0438 \u044f \u0432\u0438\u0434\u0435\u043b \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0432\u0435\u0449\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u0435\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043d\u0430\u0447\u0435. \u041f\u043b\u044e\u0441, \u043c\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430 \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0438 \u044f \u043c\u043e\u0433 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0430\u0441\u043f\u0435\u043a\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u2014 \u043e\u0442 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f, \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u044e\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0438.\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0445 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u041e\u0434\u043d\u0430 \u0438\u0437 \u0432\u0435\u0449\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u0435\u043d\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0430\u043b\u0430 \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0445 \u2014 \u044d\u0442\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0448\u0438\u0431\u043e\u043a \u043f\u043e \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u043c \u043f\u0443\u0442\u044f\u043c:\/\/ \u0422\u0438\u043f\u0438\u0447\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u2014 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044bconst errors = result.getErrors(&#8216;user.address.city&#8217;);\u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u0442\u0430\u043a? \u0415\u0441\u043b\u0438 \u044f \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u044e address \u0432 residenceAddress, \u043a\u043e\u0434 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u043d\u043e \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043d\u0443\u0442 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c\u0441\u044f. \u0421\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u043f\u0443\u0442\u0438 \u2014 \u044d\u0442\u043e \u043f\u0443\u0442\u0438 \u043a runtime-\u0431\u0430\u0433\u0430\u043c. \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0432\u044b \u0441\u043a\u0430\u0436\u0435\u0442\u0435 \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 getErrors \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u043c-\u0442\u043e \u0432\u0440\u043e\u0434\u0435 &#8216;user&#8217; | &#8216;user.address&#8217; | &#8216;user.address.city&#8217;, \u044d\u0442\u043e \u0442\u0430\u043a \u0438 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0440\u0430\u0432\u044b, \u043d\u043e \u043c\u043d\u0435 \u0432 \u0446\u0435\u043b\u043e\u043c \u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0438\u0434\u0435\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441\u043e \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438.\u041c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0447\u0435\u0433\u043e-\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0433\u043e \u043d\u0430 expression trees \u0438\u0437 C#. \u0423 \u043c\u0435\u043d\u044f \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0431\u044d\u043a\u0433\u0440\u0430\u0443\u043d\u0434 \u0432 C#, \u0438 \u0442\u0430\u043c \u0434\u0430\u0432\u043d\u043e \u0435\u0441\u0442\u044c \u043f\u0430\u0442\u0442\u0435\u0440\u043d, \u043a\u043e\u0433\u0434\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0441\u0442\u0440\u043e\u043a\u0438 \u0432\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0435 \u043b\u044f\u043c\u0431\u0434\u0443, \u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043f\u0443\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442:\/\/ \u0427\u0442\u043e \u044f \u0445\u043e\u0442\u0435\u043b \u2014 type-safe \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044bconst errors = result.getErrorsFor(t =&gt; t.user.address.city);\u0417\u0434\u0435\u0441\u044c t \u2014 \u044d\u0442\u043e \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0441\u0445\u0435\u043c\u044b. \u0415\u0441\u043b\u0438 \u044f \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u044e address, TypeScript \u0432\u044b\u0434\u0430\u0441\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438. \u0410 IDE \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0430\u0432\u0442\u043e\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432\u043f\u043b\u043e\u0442\u044c \u0434\u043e \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439.\u0418\u043c\u0435\u043d\u043d\u043e \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0440\u043e\u0441\u043b\u0430 \u043a\u043b\u044e\u0447\u0435\u0432\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u0430\u044f \u0438\u0434\u0435\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u2014 PropertyDescriptors.PropertyDescriptors: \u0441\u0445\u0435\u043c\u0430 \u043a\u0430\u043a \u0434\u0435\u0440\u0435\u0432\u043e \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u041a\u0430\u0436\u0434\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432 @cleverbrush\/schema \u044d\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u0442 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044e-\u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440, \u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432. \u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u044d\u0442\u043e\u0433\u043e \u0434\u0435\u0440\u0435\u0432\u0430 \u0437\u043d\u0430\u0435\u0442:\u041a\u0430\u043a\u043e\u043c\u0443 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0443 \u043e\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442\u041a\u0430\u043a\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u043a \u043d\u0435\u043c\u0443 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u0430\u041a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043f\u0443\u0442\u0438\u041a\u0430\u043a \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u041a\u0442\u043e \u0435\u0433\u043e \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0443\u0437\u0435\u043b\u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c type-safe \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0439 \u0433\u043b\u0443\u0431\u0438\u043d\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438:import { object, string, number, InferType } from &#8216;@cleverbrush\/schema&#8217;;const OrderSchema = object({    id: string(),    customer: object({        name: string().minLength(2),        email: string().email(),        address: object({            city: string().required(&#8216;\u0413\u043e\u0440\u043e\u0434 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d&#8217;),            zip: number()        })    }),    total: number().min(0)});\/\/ InferType \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0442\u0438\u043f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438type Order = InferType&lt;typeof OrderSchema&gt;;\/\/ \u0442.\u043a. \u0432 \u043c\u043e\u0451\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0431\u044b\u043b JS, \u0442\u043e \u044f \u0434\u0435\u043b\u0430\u043b \u0442\u0430\u043a\/\/ \/** @type {InferType&lt;typeof OrderSchema&gt;} *\/\/\/ const order = &#8230;.const result = OrderSchema.validate({    id: &#8216;123&#8217;,    customer: {        name: &#8216;A&#8217;, \/\/ \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435        email: &#8216;not-an-email&#8217;,        address: {            \/\/ city \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442            zip: -1        }    },    total: -5});if (!result.valid) {    \/\/ Type-safe \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b \u2014 IDE \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043f\u043e\u043b\u044f    const nameErrors = result.getErrorsFor(t =&gt; t.customer.name);    \/\/ { isValid: false, errors: [&#8216;\u043c\u0438\u043d\u0438\u043c\u0443\u043c 2 \u0441\u0438\u043c\u0432\u043e\u043b\u0430&#8217;], seenValue: &#8216;A&#8217; }    const cityErrors = result.getErrorsFor(t =&gt; t.customer.address.city);    \/\/ { isValid: false, errors: [&#8216;\u0413\u043e\u0440\u043e\u0434 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d&#8217;], seenValue: undefined }    \/\/ \u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0435\u0442\u0435 address \u2192 residenceAddress?    \/\/ TypeScript \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0437\u0434\u0435\u0441\u044c \u2191}\u0418\u043c\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 fluent API\u041a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u043c\u0435\u0442\u043e\u0434\u0430 \u043d\u0430 \u0441\u0445\u0435\u043c\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0431\u0438\u043b\u0434\u0435\u0440\u0430. \u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043c\u0443\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f:const base = string().minLength(2);const withMax = base.maxLength(100);    \/\/ \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440const optional = base.optional();        \/\/ \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\/\/ base, withMax \u0438 optional \u2014 \u0442\u0440\u0438 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u0441\u0445\u0435\u043c\u044b\u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u044b, \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u043a\u0430\u043a \u00ab\u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0431\u043b\u043e\u043a\u0438\u00bb \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u043c\u043e\u0434\u0443\u043b\u044f\u043c\u0438 \u0431\u0435\u0437 \u0441\u0442\u0440\u0430\u0445\u0430 \u043f\u043e\u0431\u043e\u0447\u043d\u044b\u0445 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432.\u041a\u0430\u043a\u0438\u0435 \u0431\u0438\u043b\u0434\u0435\u0440\u044b \u0435\u0441\u0442\u044c\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 14 \u0442\u0438\u043f\u043e\u0432 \u0431\u0438\u043b\u0434\u0435\u0440\u043e\u0432:\u0411\u0438\u043b\u0434\u0435\u0440\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u041f\u0440\u0438\u043c\u0435\u0440string()\u0421\u0442\u0440\u043e\u043a\u0438 \u0441 minLength, maxLength, matches, email, url, uuidstring().email().minLength(5)number()\u0427\u0438\u0441\u043b\u0430 \u0441 min, max, positive, negative, finite, multipleOfnumber().min(0).max(100)boolean()\u0411\u0443\u043b\u0435\u0432\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044fboolean().required()date()\u0414\u0430\u0442\u044b \u0441 minDate, maxDatedate().minDate(new Date())object()\u041e\u0431\u044a\u0435\u043a\u0442\u044b \u0441 \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438object({ name: string() })array()\u041c\u0430\u0441\u0441\u0438\u0432\u044b \u0441 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438array(string()).nonempty()union()\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u0432, \u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0438\u0440\u0443\u0435\u043c\u044b\u0435union(string()).or(number())tuple()\u041a\u043e\u0440\u0442\u0435\u0436\u0438 \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0434\u043b\u0438\u043d\u044btuple([string(), number()])record()\u0417\u0430\u043f\u0438\u0441\u0438 \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u043a\u043b\u044e\u0447\u0430\u043c\u0438record(string(), number())func()\u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0435\u0439func()any()\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044fany().hasType&lt;Map&gt;()nul()nullnul()lazy()\u041b\u0435\u043d\u0438\u0432\u044b\u0435 (\u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0435) \u0441\u0445\u0435\u043c\u044blazy(() =&gt; TreeSchema)extern()\u041e\u0431\u0451\u0440\u0442\u043a\u0430 \u0447\u0443\u0436\u0438\u0445 Standard Schema v1 \u0441\u0445\u0435\u043cextern(zodSchema)\u041a\u0430\u0436\u0434\u044b\u0439 \u0431\u0438\u043b\u0434\u0435\u0440 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e SchemaBuilder, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0431\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b:schema    .optional()                    \/\/ \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 undefined    .required(&#8216;\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435&#8217;) \/\/ \u043d\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 undefined    .nullable()                    \/\/ \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 null    .default(&#8216;fallback&#8217;)           \/\/ \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043f\u0440\u0438 undefined    .catch(&#8216;safe&#8217;)                 \/\/ \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043b\u044e\u0431\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438    .readonly()                    \/\/ \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0442\u0438\u043f\u043e\u0432: Readonly&lt;T&gt;    .brand&lt;&#8216;UserId&#8217;&gt;()             \/\/ branded type    .describe(&#8216;\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044f&#8217;)     \/\/ \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 (\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0441\u0445\u0435\u043c\u0435 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u044e)    .addValidator(fn)              \/\/ \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440    .addPreprocessor(fn)           \/\/ \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u0434 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0435\u0439    .introspect()                  \/\/ \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0445\u0435\u043c\u044bRuntime-\u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u044f: .introspect()\u041a\u0430\u0436\u0434\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432 @cleverbrush\/schema \u2014 \u044d\u0442\u043e \u043d\u0435 \u0447\u0451\u0440\u043d\u044b\u0439 \u044f\u0449\u0438\u043a. \u041c\u0435\u0442\u043e\u0434 .introspect() \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0445\u0435\u043c\u044b \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430: \u0442\u0438\u043f, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u0444\u043b\u0430\u0433\u0438, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f, \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439. \u0412\u0441\u0451, \u0447\u0442\u043e \u0432\u044b \u0437\u0430\u0434\u0430\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 fluent API, \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435:import { string, number, object } from &#8216;@cleverbrush\/schema&#8217;;const Name = string()    .minLength(2)    .maxLength(100)    .describe(&#8216;\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f&#8217;)    .optional();const info = Name.introspect();info.type;          \/\/ &#8216;string&#8217;info.isRequired;    \/\/ false (\u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e .optional())info.isNullable;    \/\/ falseinfo.minLength;     \/\/ 2info.maxLength;     \/\/ 100info.description;   \/\/ &#8216;\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f&#8217;info.extensions;    \/\/ {}const Age = number().min(18).default(25);const ageInfo = Age.introspect();ageInfo.type;         \/\/ &#8216;number&#8217;ageInfo.hasDefault;   \/\/ trueageInfo.defaultValue; \/\/ 25\u041a\u0430\u0436\u0434\u044b\u0439 \u0442\u0438\u043f \u0431\u0438\u043b\u0434\u0435\u0440\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 introspect() \u0441\u0432\u043e\u0438\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438: StringSchemaBuilder \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 minLength, maxLength, NumberSchemaBuilder \u2014 min, max \u0438 \u0442.\u0434.\u0417\u0430\u0447\u0435\u043c \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e? \u0418\u043c\u0435\u043d\u043d\u043e \u043d\u0430 introspect() \u043e\u043f\u0438\u0440\u0430\u0435\u0442\u0441\u044f @cleverbrush\/schema-json \u0434\u043b\u044f \u0434\u0432\u0443\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u0432 JSON Schema, @cleverbrush\/react-form \u0434\u043b\u044f \u0432\u044b\u0431\u043e\u0440\u0430 \u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440\u0430 \u043f\u043e \u0442\u0438\u043f\u0443 \u043f\u043e\u043b\u044f, \u0438 \u043b\u044e\u0431\u043e\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u00ab\u0437\u0430\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u044c\u00bb \u0441\u0445\u0435\u043c\u044b.\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0433\u0438\u0433\u0430\u043d\u0442\u0441\u043a\u0438\u0439 \u0431\u0438\u043b\u0434\u0435\u0440 \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u0430\u043c\u0438, \u044f\u0434\u0440\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u0412\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u2014 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f.\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u2014 \u044d\u0442\u043e \u043d\u0435 \u00ab\u0447\u0451\u0440\u043d\u044b\u0435 \u044f\u0449\u0438\u043a\u0438\u00bb \u0432\u0440\u043e\u0434\u0435 .refine() \u0432 Zod. \u041e\u043d\u0438 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u044b, \u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0442\u0438\u0440\u0443\u0435\u043c\u044b \u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0443\u0435\u043c\u044b:import { defineExtension, withExtensions, StringSchemaBuilder, NumberSchemaBuilder } from &#8216;@cleverbrush\/schema&#8217;;\/\/ \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u0442\u0440\u043e\u043a \u2014 HEX-\u0446\u0432\u0435\u0442const hexColorExt = defineExtension({    string: {        hexColor(this: StringSchemaBuilder) {            return this.addValidator((v) =&gt; {                const valid = \/^#[0-9a-f]{6}$\/i.test(v as string);                return {                    valid,                    errors: valid ? [] : [{ message: &#8216;\u0414\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c HEX-\u0446\u0432\u0435\u0442&#8217; }]                };            });        }    }});\/\/ \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0447\u0438\u0441\u0435\u043b \u2014 \u043f\u043e\u0440\u0442const portExt = defineExtension({    number: {        port(this: NumberSchemaBuilder) {            return this.isInteger().min(1).max(65535);        }    }});\/\/ \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0431\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0441\u0440\u0430\u0437\u0443 \u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0431\u0440\u0438\u043a\u0438const s = withExtensions(hexColorExt, portExt);\/\/ .hexColor() \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430\u0445, .port() \u2014 \u043d\u0430 \u0447\u0438\u0441\u043b\u0430\u0445const colorSchema = s.string().hexColor();const portSchema = s.number().port();\/\/ \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u043d\u044b \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0446\u0438\u044ecolorSchema.introspect().extensions; \/\/ { hexColor: true }portSchema.introspect().extensions;  \/\/ { port: true }\/\/ \u041c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u043d\u044b\u0445 \u0441\u0445\u0435\u043c\u0430\u0445const ServerConfig = s.object({    themeColor: s.string().hexColor(),    port: s.number().port(),    name: s.string().minLength(1)});\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f (email, url, uuid, positive, nonempty, oneOf \u0438 \u0434\u0440\u0443\u0433\u0438\u0435) \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0442\u0435\u043c \u0436\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u043c \u2014 \u043e\u043d\u0438 \u043d\u0435 \u0432\u0445\u043e\u0434\u044f\u0442 \u0432 \u044f\u0434\u0440\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.Standard Schema v1:&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-475791","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/475791","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=475791"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/475791\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=475791"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=475791"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=475791"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}