{"id":352232,"date":"2024-05-20T22:04:43","date_gmt":"2024-05-20T22:04:43","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=352232"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-05-20T22:04:43","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=352232","title":{"rendered":"<span>\u0420\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u044f \u0432 JavaScript \u0438 TypeScript: \u043e\u0431\u0437\u043e\u0440 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0442\u0435\u0445\u043d\u0438\u043a. \u041a\u0430\u043a \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c CLI-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u043a\u043b\u0430\u0441\u0441\u0430<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\u041a\u0430\u043a \u0438 \u0432 \u043b\u044e\u0431\u043e\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435, \u0432 JavaScript \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0435\u0441\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u2014 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0438\u043f\u044b, \u043a\u043b\u044e\u0447\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u044b \u0438 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u044b.<\/p>\n<p>  \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0442\u0430\u043a\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438, \u043f\u043b\u044e\u0441 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0435\u0449\u0451 \u0431\u043e\u043b\u044c\u0448\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0442\u0438\u043f\u0430\u0445 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 TypeScript, \u0438 \u043a\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u0430\u043c \u0438 \u0438\u0445 \u043f\u043e\u043b\u044f\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432. \u041a\u0430\u0436\u0434\u0443\u044e \u0438\u0437 \u0442\u0435\u0445\u043d\u0438\u043a \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e CLI-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043a \u043a\u043e\u043d\u0446\u0443 \u0441\u0442\u0430\u0442\u044c\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u0430\u043a \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435:<\/p>\n<p>  <a href=\"https:\/\/habr.com\/ru\/companies\/ruvds\/articles\/754764\/\"><\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/te\/d6\/lq\/ted6lqrchhdoezwwrmw9cxpsmjy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/te\/d6\/lq\/ted6lqrchhdoezwwrmw9cxpsmjy.png\"\/><\/div>\n<p><\/a><a name=\"habracut\"><\/a>  <\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 0: \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u0438<\/font><\/h2>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0434 \u0432\u043e\u043e\u0431\u0449\u0435 \u0431\u0435\u0437 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u0438 \u2014 \u043f\u043e \u0444\u0430\u043a\u0442\u0443, \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u0434\u043b\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e <a href=\"https:\/\/nodejs.org\/docs\/latest-v20.x\/api\/util.html#utilparseargsconfig\">util.parseArgs<\/a> \u0438\u0437 Node.js.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage0\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { parseArgs } from \"node:util\";  export type Main = (   args: string[],   opts: Record&lt;string, OptionValue>, ) => void | number | Promise&lt;void | number>;  export type OptionValue =   \/\/ \u043e\u043f\u0446\u0438\u044f \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0430   | undefined   \/\/ \u043e\u043f\u0446\u0438\u044f-\u0444\u043b\u0430\u0433, \u0431\u0435\u0437 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f   | boolean   \/\/ \u043e\u043f\u0446\u0438\u044f \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c   | string   \/\/ \u043e\u043f\u0446\u0438\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437   | Array&lt;boolean | string>;  export async function run(main: Main) {   const { positionals: args, values: opts } = parseArgs({ strict: false });    try {     const code = await main(args, opts);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0430\u043a\u043e\u0433\u043e \u043d\u0435\u0434\u043e-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage0\/main.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { OptionValue, run } from \".\/framework.js\";  await run(main);  function main(args: string[], opts: Record&lt;string, OptionValue>) {   \/\/ \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0435 \u0438\u043c\u0435\u043d\u0430   if (opts.verbose || opts.v) {     console.debug(args);     console.debug(opts);   }    \/\/ \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b   const [command, ...commandArgs] = args;   if (!command) {     console.error(\"no command specified\");     return 1;   }    switch (command) {     case \"hello\": {       const [name] = commandArgs;       if (!name) {         console.error(`command required 1 argument, 0 given`);         return 1;       }        \/\/ \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u0438\u043f\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043e\u043f\u0446\u0438\u0439       const enthusiastic = opts.enthusiastic ?? opts.e ?? false;       if (typeof enthusiastic !== \"boolean\") {         console.error(`invalid type for --enthusiastic option`);         return 1;       }        console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);       return 0;     }      default:       console.error(`unknown command: ${command}`);       return 1;   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041a\u0430\u043a \u0445\u043e\u0440\u043e\u0448\u043e \u0432\u0438\u0434\u043d\u043e \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0447\u0442\u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c CLI-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u041a\u043e\u0440\u043e\u0442\u043a\u0438\u0435 \u0438\u043c\u0435\u043d\u0430 \u043e\u043f\u0446\u0438\u0439, \u0442\u0438\u043f\u044b \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043a\u043e\u043c\u0430\u043d\u0434 \u2014 \u0432\u0441\u0451 \u044d\u0442\u043e \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e.<\/p>\n<p>  \u042d\u0442\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0438\u043c\u0435\u043d\u043d\u043e \u043c\u043e\u0435\u0433\u043e \u043a\u043e\u0434\u0430 \u2014 \u0441\u0430\u043c\u043e\u0439 <code>parseArgs<\/code> \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c <a href=\"https:\/\/nodejs.org\/docs\/latest-v20.x\/api\/util.html#utilparseargsconfig\">\u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 CLI-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430<\/a> \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f\u043c\u0438 \u0432\u0441\u0435\u0445 \u043e\u043f\u0446\u0438\u0439. \u041d\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435, \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u044e, \u043f\u043e\u0437\u0432\u043e\u043b\u044f \u0435\u043c\u0443 \u0441\u0430\u043c\u043e\u043c\u0443 \u0432\u044b\u0432\u0435\u0441\u0438\u0442\u044c \u044d\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435.<\/p>\n<p>  \u041d\u0430\u0447\u043d\u0443 \u0441 \u043e\u0441\u043d\u043e\u0432.<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 1: \u043e\u0441\u043d\u043e\u0432\u044b JS-\u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u0438<\/font><\/h2>\n<p>  \u042d\u0442\u0438 \u0442\u0435\u0445\u043d\u0438\u043a\u0438 \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u044b, \u0447\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043a JS \u0438\u0445 \u0440\u0435\u0434\u043a\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u0435\u0439:<\/p>\n<ul>\n<li> <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/typeof\">\u041e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 typeof<\/a>: \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 JS-\u0442\u0438\u043f\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f.\n<p>  \u0412\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 <code>typeof x<\/code> \u043c\u043e\u0436\u0435\u0442 \u0432\u0435\u0440\u043d\u0443\u0442\u044c <code>\"undefined\"<\/code>, <code>\"boolean\"<\/code>, <code>\"number\"<\/code>, <code>\"bigint\"<\/code>, <code>\"string\"<\/code>, <code>\"symbol\"<\/code>, <code>\"object\"<\/code>, <code>\"function\"<\/code>. \u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u0447\u0442\u043e \u043f\u043e \u0438\u0441\u0442\u043e\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c <code>typeof null === \"object\"<\/code>!<\/p>\n<p>  \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0434\u043b\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f <code>\"function\"<\/code> \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u0438, \u0447\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043a\u0430\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0438\u0445 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 <code>new<\/code>.  <\/li>\n<li> <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/instanceof\">\u041e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 instanceof<\/a>: \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435, \u0435\u0441\u0442\u044c \u043b\u0438 \u043d\u0443\u0436\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.\n<p>  \u0415\u0441\u043b\u0438 \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u0440\u043e \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u043d\u043e\u0435 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u043e\u043f\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438, \u0442\u043e <code>x instanceof A<\/code> \u0432\u0435\u0440\u043d\u0451\u0442 \u0431\u0443\u043b\u0435\u0432\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0435, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 <code>x<\/code> \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u043c <code>A<\/code> \u0438\u043b\u0438 \u0435\u0433\u043e \u043f\u043e\u0442\u043e\u043c\u043a\u0430.  <\/li>\n<li> <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/in\">\u041e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 in<\/a>: \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u043a\u043b\u044e\u0447\u0430 \u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.\n<p>  \u0412\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 <code>\"p\" in x<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0435\u0441\u0442\u044c \u043b\u0438 \u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>x<\/code> \u043a\u043b\u044e\u0447 <code>p<\/code>. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c <code>x<\/code> \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c, \u0438\u043d\u0430\u0447\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043a\u0438\u043d\u0443\u0442\u0430 <code>TypeError<\/code>.  <\/li>\n<li> <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Object\/keys\">\u0424\u0443\u043d\u043a\u0446\u0438\u044f Object.keys()<\/a> \u0438 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Statements\/for...in\">\u0446\u0438\u043a\u043b for&#8230;in<\/a>: \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0435\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.\n<p>  \u042d\u0442\u0438\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Enumerability_and_ownership_of_properties\">\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u044f\u0435\u043c\u044b\u043c\u0438 (enumerable)<\/a>. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0432 \u044d\u0442\u0443 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442 \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u044c. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u0434\u0430\u043b\u0435\u0435.  <\/li>\n<\/ul>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u043d\u0438\u0445, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430\u0448 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0447\u0443\u0442\u044c \u043a\u0440\u0430\u0441\u0438\u0432\u0435\u0435. \u0410 \u0438\u043c\u0435\u043d\u043d\u043e: \u043f\u0443\u0441\u0442\u044c \u0442\u0435\u043f\u0435\u0440\u044c \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u043c, \u0430 \u0435\u0433\u043e \u043f\u043e\u043b\u044f \u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u043e\u0431\u0449\u0438\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 \u043e\u043f\u0446\u0438\u0438:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage1\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { parseArgs } from \"node:util\";  export interface Program {   main(     args: string[],     opts: Record&lt;string, OptionValue>,   ): void | number | Promise&lt;void | number>; }  export type OptionValue =   | undefined   | boolean   | string   | Array&lt;boolean | string>;  export async function run(Program: new () => Program) {   const program = new Program();    const { positionals: args, values: opts } = parseArgs({ strict: false });    \/\/ \u043f\u043e \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044e, \u0432\u0441\u0435 \u043f\u043e\u043b\u044f `program` - \u044d\u0442\u043e \u043e\u0431\u0449\u0438\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 \u043e\u043f\u0446\u0438\u0438   for (const k of Object.keys(program)) {     if (k in opts) {       \/\/ \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u044e, \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e       \/\/ \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0433\u043e\u0442\u043e\u0432\u044c\u0442\u0435\u0441\u044c \u2014 \u0432 \u043a\u043e\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 any       (program as any)[k] = opts[k];       delete opts[k];     }   }    try {     const code = await program.main(args, opts);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041a\u043e\u0434 \u0432 <code>main.ts<\/code> \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage1\/main.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { OptionValue, run } from \".\/framework.js\";  class Program {   \/\/ \u0435\u0441\u043b\u0438 target \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0441\u0442\u0430\u0440\u044b\u0439 (\u043c\u0435\u043d\u044c\u0448\u0435 es2022),   \/\/ \u0442\u043e \u0432 \u043d\u0451\u043c \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u0430 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439   \/\/ \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043d\u0435 \u0437\u0430\u0434\u0430\u043d\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0435   verbose: boolean | undefined = undefined;    \/\/ \u0434\u043b\u044f es2022 \u0438 \u043d\u043e\u0432\u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e:   v: boolean | undefined;    main(args: string[], opts: Record&lt;string, OptionValue>) {     \/\/ \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0435 \u0438\u043c\u0435\u043d\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e     const verbose = this.verbose ?? this.v ?? false;     if (verbose) {       console.debug(this);       console.debug(args);       console.debug(opts);     }      \/\/ \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0451 \u0435\u0449\u0451 \u0432\u0440\u0443\u0447\u043d\u0443\u044e     const [command, ...commandArgs] = args;     if (!command) {       console.error(\"no command specified\");       return 1;     }      switch (command) {       case \"hello\": {         const [name] = commandArgs;         if (!name) {           console.error(`command required 1 argument, 0 given`);           return 1;         }          \/\/ \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u0438\u043f\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043e\u043f\u0446\u0438\u0439         const enthusiastic = opts.enthusiastic ?? opts.e ?? false;         if (typeof enthusiastic !== \"boolean\") {           console.error(`invalid type for --enthusiastic option`);           return 1;         }          console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);         return 0;       }        default:         console.error(`unknown command: ${command}`);         return 1;     }   } }  await run(Program); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u043d\u0435 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0437\u0430\u043c\u0435\u0442\u043d\u044b: \u043e\u0431\u0449\u0438\u0435 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434 \u043e\u043f\u0446\u0438\u0438 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438, \u043d\u043e \u0441\u0430\u043c\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u0438\u0441\u043f\u0430\u0442\u0447\u0438\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041d\u043e \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0443\u0440\u043e\u0432\u043d\u0435\u043c \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u0438!<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 2: \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u044b, \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u043e\u0432<\/font><\/h2>\n<p>  \u0420\u0430\u0441\u0448\u0438\u0440\u0438\u043c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f <code>Program<\/code>: \u0432\u0441\u0435 \u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438.<\/p>\n<p>  \u041c\u0435\u0442\u043e\u0434\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 JS \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0435\u0433\u043e \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0430, \u0443 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u2014 \u044d\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u041f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043a\u043b\u0430\u0441\u0441\u0430 <code>A<\/code> \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043a\u0430\u043a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Function\/prototype\">A.prototype<\/a>.<\/p>\n<p>  \u041e\u0434\u043d\u0430\u043a\u043e \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043d\u0435 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u043e\u0432 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u0430 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043c\u0435\u0442\u043e\u0434\u044b <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Functions\/Method_definitions#method_definitions_in_classes\">\u043e\u0431\u044a\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u044f\u0435\u043c\u044b\u043c\u0438<\/a>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c <code>Object.keys(Program.prototype)<\/code> \u0438\u043b\u0438 <code>for (k in Program.prototype)<\/code> \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u041d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Object\/getOwnPropertyNames\">Object.getOwnPropertyNames()<\/a>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 <em>\u0432\u0441\u0435<\/em> \u043a\u043b\u044e\u0447\u0438 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430.<\/p>\n<p>  \u0423 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0435\u0441\u0442\u044c \u0435\u0449\u0451 \u043e\u0434\u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 <code>Object.keys()<\/code>. \u041d\u0430 \u043d\u0435\u0451 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 <code>Own<\/code> \u0432 \u0438\u043c\u0435\u043d\u0438 \u2014 \u043e\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043a\u043b\u044e\u0447\u0438, \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0430\u0449\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u044d\u0442\u043e\u043c\u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0443, <em>\u043d\u0435<\/em> \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u044f\u0441\u044c \u043f\u043e \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u043e\u0432 \u2014 \u0442\u043e \u0435\u0441\u0442\u044c \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u043b\u044e\u0447\u0438. \u0415\u0441\u043b\u0438 \u043e\u043d\u0438 \u0432\u0441\u0451-\u0442\u0430\u043a\u0438 \u043d\u0443\u0436\u043d\u044b, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u043e \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u043e\u0432 \u0441\u0430\u043c\u0438\u043c \u2014 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"javascript\">const allKeys = []; for (let proto = A.prototype; proto; proto = Object.getPrototypeOf(proto)) {   allKeys.push(...Object.getOwnPropertyNames(proto)); } <\/code><\/pre>\n<p>  \u0412 \u043d\u0430\u0448\u0435\u043c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043b\u0430\u0441\u0441\u0430 <code>Program<\/code>, \u043d\u0435 \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043e\u0442 \u043f\u0440\u0435\u0434\u043a\u043e\u0432. \u0422\u0430\u043a \u043d\u0430\u043c, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435, \u043d\u0435 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0431\u0435\u0441\u043f\u043e\u043a\u043e\u0438\u0442\u044c\u0441\u044f \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u0430\u043a \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u043e\u0431\u0449\u0435\u0433\u043e \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u043f\u0440\u0435\u0434\u043a\u0430 <code>Object<\/code>.<\/p>\n<p>  \u0412\u0430\u0436\u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u0447\u0442\u043e <code>constructor<\/code> \u2014 \u044d\u0442\u043e \u0442\u043e\u0436\u0435 \u043a\u043b\u044e\u0447 \u0432 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0435 \u043b\u044e\u0431\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u0415\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage2\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { parseArgs } from \"node:util\";  export type CommandFn = (   args: string[],   opts: Record&lt;string, OptionValue>, ) => void | number | Promise&lt;void | number>;  export type OptionValue =   | undefined   | boolean   | string   | Array&lt;boolean | string>;  export async function run(Program: new () => any) {   const program = new Program();    const {     positionals: [command, ...args],     values: opts,   } = parseArgs({ strict: false });    const sharedOpts = Object.keys(program);    for (const k of sharedOpts) {     if (k in opts) {       program[k] = opts[k];       delete opts[k];     }   }    \/\/ \u043f\u043e \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044e, \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b `program` - \u044d\u0442\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u044b   \/\/ \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043b\u0430\u0441\u0441\u0430 \u043d\u0435 enumerable   \/\/ \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 getOwnPropertyNames(), \u0430 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e keys()   const commands = Object.getOwnPropertyNames(Program.prototype).filter(     (k) => typeof program[k] === \"function\" &amp;&amp; k !== \"constructor\",   );    \/\/ \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430    if (!command) {     console.error(\"no command specified\");     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    if (!commands.includes(command)) {     console.error(`unknown command: ${command}`);     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    try {     const code = await program[command]!(args, opts);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u043c\u043e\u0436\u0435\u043c \u0443\u0431\u0440\u0430\u0442\u044c \u0438\u0437 <code>main.ts<\/code> \u043a\u043e\u0434 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u043c\u0430\u043d\u0434:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage2\/main.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { OptionValue, run } from \".\/framework.js\";  class Program {   verbose: boolean | undefined;   v: boolean | undefined;    hello(args: string[], opts: Record&lt;\"e\" | \"enthusiastic\", OptionValue>) {     const verbose = this.verbose ?? this.v ?? false;     if (verbose) {       console.debug(this);       console.debug(args);       console.debug(opts);     }      const enthusiastic = opts.enthusiastic ?? opts.e ?? false;     if (typeof enthusiastic !== \"boolean\") {       console.error(`invalid type for --enthusiastic option`);       return 1;     }      const [name] = args;     if (!name) {       console.error(`command required 1 argument, 0 given`);       return 1;     }      console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);     return 0;   } }  await run(Program); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 3: \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0439<\/font><\/h2>\n<p>  \u0425\u043e\u0440\u043e\u0448\u043e \u0431\u044b \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 <code>args<\/code> \u0441\u0430\u043c\u0438\u043c, \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0447\u0442\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441\u0430\u043c\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432-\u043a\u043e\u043c\u0430\u043d\u0434: \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043d\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c, \u0430 \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043c\u0435\u0442\u043e\u0434\u0430, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0432 <code>opts<\/code> \u043d\u0430 \u043f\u0435\u0440\u0432\u043e\u0435 \u043c\u0435\u0441\u0442\u043e:<\/p>\n<pre><code class=\"javascript\">\/\/ \u0411\u044b\u043b\u043e: hello(args: string[], opts: Record&lt;string, OptionValue>): ...  \/\/ \u0421\u0442\u0430\u043b\u043e: hello(opts: Record&lt;string, OptionValue>, name: string): ... <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0445 CLI-\u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0415\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>f<\/code> \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Function\/length\">\u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 f.length<\/a>.<\/p>\n<p>  \u041d\u043e \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u0445\u0438\u0442\u0440\u043e\u0441\u0442\u044c. \u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>f.length<\/code> \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u0431\u0443\u0434\u0435\u0442 <em>\u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c<\/em> \u0447\u0438\u0441\u043b\u043e\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438! \u041e\u043d\u043e \u043d\u0435 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0438 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<pre><code class=\"javascript\">\/\/ \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e function f1(a, b = null) {} assert(f1.length === 1);  \/\/ rest-\u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b function f2(a, ...bs) {} assert(f2.length === 1);  \/\/ \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e arguments function f3(a) {   doWork(arguments[2]); } assert(f3.length === 1);  \/\/ \u043f\u043b\u044e\u0441 \u043a \u044d\u0442\u043e\u043c\u0443, \"\u043b\u0438\u0448\u043d\u0438\u0435\" \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f function f4(a) {} f4(1, 2, 3, 4, 5); <\/code><\/pre>\n<p>  \u0423\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u044d\u0442\u043e, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0447\u0438\u0441\u043b\u0430 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage3\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { parseArgs } from \"node:util\";  export type CommandFn = (   opts: Record&lt;string, OptionValue>,   \/\/ rest-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c   ...args: string[] ) => void | number | Promise&lt;void | number>;  export type OptionValue =   | undefined   | boolean   | string   | Array&lt;boolean | string>;  export async function run(Program: new () => any) {   const program = new Program();    const {     positionals: [command, ...args],     values: opts,   } = parseArgs({ strict: false });    const sharedOpts = Object.keys(program);    for (const k of sharedOpts) {     if (k in opts) {       program[k] = opts[k];       delete opts[k];     }   }    \/\/ \u043f\u043e \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044e, \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b `program` - \u044d\u0442\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u044b   \/\/ \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043b\u0430\u0441\u0441\u0430 \u043d\u0435 enumerable   \/\/ \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 getOwnPropertyNames(), \u0430 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e keys()   const commands = Object.getOwnPropertyNames(Program.prototype).filter(     (k) => typeof program[k] === \"function\" &amp;&amp; k !== \"constructor\",   );    \/\/ \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430    if (!command) {     console.error(\"no command specified\");     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    if (!commands.includes(command)) {     console.error(`unknown command: ${command}`);     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    const commandFn: Function = program[command];    \/\/ \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c \u0447\u0438\u0441\u043b\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438   \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u043e\u0436\u043d\u043e, \u043c\u0435\u043d\u044c\u0448\u0435 \u043d\u0435\u0442   \/\/ +1 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0441 \u043e\u043f\u0446\u0438\u044f\u043c\u0438    const minArgCount = commandFn.length - 1;    if (args.length &lt; minArgCount) {     console.error(`too few arguments for command ${command}`);     console.error(`at least ${minArgCount}, ${args.length} given`);      process.exitCode = 1;     return;   }    try {     const code = await program[command]!(opts, ...args);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage3\/main.js<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { OptionValue, run } from \".\/framework.js\";  class Program {   verbose: boolean | undefined;   v: boolean | undefined;    hello(opts: Record&lt;\"e\" | \"enthusiastic\", OptionValue>, name: string) {     const verbose = this.verbose ?? this.v ?? false;     if (verbose) {       console.debug(this);       console.debug([name]);       console.debug(opts);     }      const enthusiastic = opts.enthusiastic ?? opts.e ?? false;     if (typeof enthusiastic !== \"boolean\") {       console.error(`invalid type for --enthusiastic option`);       return 1;     }      console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);     return 0;   } }  await run(Program); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 4: \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b \u0438 Reflect.metadata<\/font><\/h2>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0443\u043c\u0435\u043b \u0441\u0430\u043c \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u043a\u0430\u043a\u043e\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u0438\u043c\u044f \u0435\u0441\u0442\u044c \u0443 \u043e\u043f\u0446\u0438\u0438, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0432\u0435\u0441\u0438\u0442\u044c \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u0438 \u044d\u0442\u043e \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u0438\u043c\u044f, \u0438 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0435\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u043c \u044f\u0432\u043d\u043e \u043e\u0442\u043c\u0435\u0447\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b-\u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430-\u043e\u043f\u0446\u0438\u0438, \u0442\u043e \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u043c\u0435\u0442\u044c \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 <code>Program<\/code> \u0438 \u043f\u043e\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u043c\u0435\u0442\u043e\u0434\u044b.<\/p>\n<p>  \u041f\u0440\u043e\u0449\u0435 \u0438 \u043a\u0440\u0430\u0441\u0438\u0432\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b.<\/p>\n<p>  \u0423 <a href=\"https:\/\/github.com\/tc39\/proposal-decorators\">\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432<\/a> \u0432 JS \u0438 TS \u0442\u044f\u0436\u0451\u043b\u0430\u044f \u0441\u0443\u0434\u044c\u0431\u0430. \u041f\u0440\u043e\u043f\u043e\u0437\u0430\u043b \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u043f\u0435\u0440\u0435\u0434\u0435\u043b\u044b\u0432\u0430\u043b\u0438, \u0438 \u043c\u043d\u043e\u0433\u0438\u0435 \u043a\u043e\u0434\u043e\u0432\u044b\u0435 \u0431\u0430\u0437\u044b \u0432\u0441\u0451 \u0435\u0449\u0451 \u0437\u0430\u0432\u044f\u0437\u0430\u043d\u044b \u043d\u0430 \u043f\u043e\u043b\u0438\u0444\u0438\u043b\u043b\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0445 \u0434\u0440\u0430\u0444\u0442\u043e\u0432 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<p>  \u0412 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0435 TypeScript \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0434\u0432\u0430 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432:<\/p>\n<ul>\n<li>\u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 (\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0431\u0435\u0437 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043e\u043f\u0446\u0438\u0439 <a href=\"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-5-0\/#decorators\">\u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 TypeScript 5.0<\/a>);<\/li>\n<li>\u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0447\u0435\u0440\u043d\u043e\u0432\u0438\u043a\u043e\u0432 (\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/decorators.html\">\u0441 \u043e\u043f\u0446\u0438\u0435\u0439 experimentalDecorators<\/a>).<\/li>\n<\/ul>\n<p>  \u0417\u0430\u0431\u0435\u0433\u0430\u044f \u0432\u043f\u0435\u0440\u0451\u0434, \u0441\u043a\u0430\u0436\u0443, \u0447\u0442\u043e \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0445 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u0438 \u0432 TS \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u043d\u043e <code>experimentalDecorators<\/code>. \u041d\u043e \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0443\u0440\u043e\u0432\u043d\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0430\u0431\u0441\u0442\u0440\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u0431\u043e\u0440\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/www.npmjs.com\/package\/reflect-metadata\">reflect-metadata<\/a>:<\/p>\n<pre><code class=\"javascript\">import \"reflect-metadata\"; \/\/ \u0442\u0435\u043f\u0435\u0440\u044c \u0432 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u0435 Reflect \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043d\u043e\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b  class Foo {   \/\/ \u043c\u0435\u0442\u043e\u0434 Reflect.metadata() \u043c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430   @Reflect.metadata(\"meta-key\", \"value\")   f() {} }  \/\/ \u043d\u043e \u043b\u0443\u0447\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0435\u0433\u043e \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e const MyDecorator = (value) =>   \/\/ \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043a\u043b\u044e\u0447\u0430 \u0443\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u0443 \u044d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e   Reflect.metadata(MyDecorator, value);  class Bar {   @MyDecorator(\"value\")   prop: string; }  \/\/ \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0447\u0438\u0442\u0430\u0442\u044c \u0432\u043e\u0442 \u0442\u0430\u043a: const value1 = Reflect.getMetadata(Foo.prototype, \"meta-key\", \"f\"); const value2 = Reflect.getMetadata(Bar.prototype, MyDecorator, \"prop\");  \/\/ \u0435\u0441\u043b\u0438 \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442, \/\/ \u0432\u0435\u0440\u043d\u0443\u0442\u0441\u044f \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435, \u043d\u0430\u0432\u0435\u0448\u0435\u043d\u043d\u044b\u0435 \u043d\u0430 \u0441\u0430\u043c \u043a\u043b\u0430\u0441\u0441, \u0430 \u043d\u0435 \u043d\u0430 \u0435\u0433\u043e \u0447\u043b\u0435\u043d\u044b <\/code><\/pre>\n<p>  \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b, <code>reflect-metadata<\/code> \u0438 \u043e\u0431\u0445\u043e\u0434 \u043a\u043b\u044e\u0447\u0435\u0439 \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0443\u0440\u043e\u0432\u043d\u0435\u0439, \u043d\u0435\u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u0443\u044e \u043d\u0430\u043c \u0444\u0438\u0447\u0443:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage4\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import \"reflect-metadata\"; import { parseArgs } from \"node:util\";  export interface OptionDefinition {   short?: string; }  \/\/ \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Reflect.metadata \/\/ \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043a\u043b\u044e\u0447\u0430 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0434\u043e\u0431\u043d\u043e \u0431\u0440\u0430\u0442\u044c \u0441\u0430\u043c\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e-\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 export const Option = (def: OptionDefinition) => Reflect.metadata(Option, def);  \/\/ \u0434\u043b\u044f \u043b\u0443\u0447\u0448\u0435\u0439 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0438 \u0443\u0434\u043e\u0431\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0433\u0435\u0442\u0442\u0435\u0440 const getOptionMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Option, ctor.prototype, prop) as     | OptionDefinition     | undefined;  \/\/ \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0443 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432, \/\/ \u0435\u0433\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 export const Command = () => Reflect.metadata(Command, {});  const getCommandMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Command, ctor.prototype, prop) as {} | undefined;  export type CommandFn = (   opts: Record&lt;string, OptionValue>,   ...args: string[] ) => void | number | Promise&lt;void | number>;  export type OptionValue =   | undefined   | boolean   | string   | Array&lt;boolean | string>;  export async function run(Program: new () => any) {   const program = new Program();    const {     positionals: [command, ...args],     values: opts,   } = parseArgs({ strict: false });    for (const k of Object.keys(program)) {     const def = getOptionMetadata(Program, k);     if (!def) continue;      if (def.short &amp;&amp; def.short in opts) {       program[k] = opts[def.short];       delete opts[def.short];     }      if (k in opts) {       program[k] = opts[k];       delete opts[k];     }   }    const commands: string[] = [];    for (const k of Object.getOwnPropertyNames(Program.prototype)) {     const def = getCommandMetadata(Program, k);     if (!def) continue;      commands.push(k);   }    if (!command) {     console.error(\"no command specified\");     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    if (!commands.includes(command)) {     console.error(`unknown command: ${command}`);     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    const commandFn: Function = program[command];   const minArgCount = commandFn.length - 1;    if (args.length &lt; minArgCount) {     console.error(`too few arguments for command ${command}`);     console.error(`at least ${minArgCount}, ${args.length} given`);      process.exitCode = 1;     return;   }    try {     const code = await program[command]!(opts, ...args);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043a\u043e\u0434 \u0432 <code>main.ts<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage4\/main.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { Command, Option, OptionValue, run } from \".\/framework.js\";  class Program {   \/\/ \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043e\u043f\u0446\u0438\u0438 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u0438\u043c\u044f   @Option({ short: \"v\" })   verbose = false;    \/\/ \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u043f\u043e\u043b\u044f, \u043d\u0435 \u044f\u0432\u043b\u044f\u044e\u0449\u0438\u0435\u0441\u044f \u043e\u043f\u0446\u0438\u044f\u043c\u0438   version = \"1.0.0\";    @Command()   hello(opts: Record&lt;\"e\" | \"enthusiastic\", OptionValue>, name: string) {     if (this.verbose) {       console.debug(this);       console.debug([name]);       console.debug(opts);     }      \/\/ \u043e\u043f\u0446\u0438\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c \u0440\u0443\u043a\u0430\u043c\u0438     const enthusiastic = opts.enthusiastic ?? opts.e ?? false;     if (typeof enthusiastic !== \"boolean\") {       console.error(`invalid type for --enthusiastic option`);       return 1;     }      console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);     return 0;   } }  await run(Program); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 5: \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u0438\u043f\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0430<\/font><\/h2>\n<p>  \u041d\u0435\u043f\u043b\u043e\u0445\u043e \u0431\u044b \u0432 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <code>@Option()<\/code> \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0436\u0435 \u0442\u0438\u043f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043e\u043f\u0446\u0438\u0438. \u041d\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0438\u043f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 JS \u0434\u0440\u0443\u0433\u0438\u043c JS-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c, \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0435\u0442. \u0415\u0441\u0442\u044c, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0442\u043e, \u0447\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>typeof<\/code>, \u043d\u043e \u044d\u0442\u043e\u0433\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u2014 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u0432.<\/p>\n<p>  \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e, \u0435\u0441\u0442\u044c \u0440\u044f\u0434 \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0451\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u0438 \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 \u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438. \u0412 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438, \u044f \u0431\u0443\u0434\u0443 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 <a href=\"https:\/\/docs.nestjs.com\/openapi\/types-and-parameters#arrays\">\u043f\u043e\u0432\u0441\u0435\u043c\u0435\u0441\u0442\u043d\u043e<\/a> <a href=\"https:\/\/docs.nestjs.com\/techniques\/mongodb#model-injection\">\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f<\/a> <a href=\"https:\/\/docs.nestjs.com\/\">\u0432 Nest.js<\/a>:<\/p>\n<pre><code class=\"javascript\">\/\/ \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0438\u0445 \"\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u043e\u043c\" const number = Number; const string = String;  \/\/ \u043c\u0430\u0441\u0441\u0438\u0432\u044b \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u043e\u0434\u043d\u043e\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043d\u044b\u043c\u0438 \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c\u0438 const arrayOfNumber = [Number]; const arrayOfString = [String];  \/\/ \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b, \u043a\u0445\u043c, \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 const person = {   name: String,   age: Number, };  \/\/ \u0438 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043b\u044e\u0431\u044b\u0445 \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044f\u0445 const dto = {   people: [{ name: String, age: Number }], };  \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441, \u043f\u043e\u0445\u043e\u0436\u0438\u0439 \u043d\u0430 \u0442\u0438\u043f\u044b \u0432 TypeScript type Dto = {   people: { name: string; age: number }[]; }; <\/code><\/pre>\n<p>  \u0412 TypeScript \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0442\u0430\u043a\u0438\u043c \u0440\u0430\u043d\u0442\u0430\u0439\u043c-\u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0442\u0438\u043f\u043e\u0432 \u0432\u0430\u0436\u043d\u043e \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0442\u044c, \u0433\u0434\u0435 \u043e\u043d\u043e, \u0430 \u0433\u0434\u0435 \u0442\u0438\u043f\u044b \u0441\u0430\u043c\u043e\u0433\u043e TypeScript. \u0415\u0441\u043b\u0438, \u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443, \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043e\u0431\u044a\u044f\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u0435 \u043a\u0430\u043a\u043e\u0433\u043e-\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043a\u0430\u043a <code>Number<\/code> \u0432\u043c\u0435\u0441\u0442\u043e <code>number<\/code>, \u0442\u043e \u043e\u0448\u0438\u0431\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0441\u043a\u043e\u0447\u0438\u0442\u044c \u0432 \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u2014 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 <a href=\"https:\/\/www.typescriptlang.org\/play#code\/DYUwLgBAhgXBByBXAtgIxAJwgXggRgCYBmAbgChRJU4A7FdLXAFgFYA2csqHCVc1HlBJA\">\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0441\u0432\u043e\u0438\u0442\u044c<\/a> \u043a \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439, \u0442\u0438\u043f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u2014 \u0435\u0433\u043e boxed-\u0432\u0435\u0440\u0441\u0438\u044f. \u041d\u043e \u043d\u0435 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442!<\/p>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0434\u043b\u044f \u0442\u0438\u043f\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043d\u0430\u0448 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0442\u0438\u043f\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043e\u043f\u0446\u0438\u0439. \u041e\u0431\u043b\u0435\u0433\u0447\u0438\u0442 \u043d\u0430\u043c \u0437\u0430\u0434\u0430\u0447\u0443 \u0442\u043e, \u0447\u0442\u043e <code>parseArgs<\/code> \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0442\u044b\u0440\u0435 \u0442\u0438\u043f\u0430: <code>boolean | string | boolean[] | string[]<\/code>:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage5\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import \"reflect-metadata\"; import { ParseArgsConfig, parseArgs } from \"node:util\";  export interface OptionDefinition {   short?: string;    \/\/ \u0442\u0430\u043a\u043e\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0434\u043b\u044f \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u0432 \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435 \u0443\u0436\u0435 \u0441\u0442\u0430\u043b \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u043c \u0434\u0435-\u0444\u0430\u043a\u0442\u043e   type: typeof String | typeof Boolean | [typeof String] | [typeof Boolean]; }  export const Option = (def: OptionDefinition) => Reflect.metadata(Option, def);  const getOptionMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Option, ctor.prototype, prop) as     | OptionDefinition     | undefined;  export const Command = () => Reflect.metadata(Command, {});  const getCommandMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Command, ctor.prototype, prop) as {} | undefined;  export type CommandFn = (   opts: Record&lt;string, OptionValue>,   ...args: string[] ) => void | number | Promise&lt;void | number>;  export type OptionValue =   | undefined   | boolean   | string   | Array&lt;boolean | string>;  export async function run(Program: new () => any) {   const program = new Program();    const {     positionals: [command, ...args],     values: opts,   } = parseArgs({     strict: false,     options: getOptionsConfigFromMetadata(Program, program),   });    for (const k of Object.keys(program)) {     const def = getOptionMetadata(Program, k);     if (!def) continue;      if (def.short &amp;&amp; def.short in opts) {       program[k] = opts[def.short];       delete opts[def.short];     }      if (k in opts) {       program[k] = opts[k];       delete opts[k];     }   }    const commands: string[] = [];    for (const k of Object.getOwnPropertyNames(Program.prototype)) {     const def = getCommandMetadata(Program, k);     if (!def) continue;      commands.push(k);   }    if (!command) {     console.error(\"no command specified\");     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    if (!commands.includes(command)) {     console.error(`unknown command: ${command}`);     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    const commandFn: Function = program[command];   const minArgCount = commandFn.length - 1;    if (args.length &lt; minArgCount) {     console.error(`too few arguments for command ${command}`);     console.error(`at least ${minArgCount}, ${args.length} given`);      process.exitCode = 1;     return;   }    try {     const code = await program[command]!(opts, ...args);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } }  \/\/ \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u044d\u0442\u043e\u0442 \u0442\u0438\u043f \u043d\u0435 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043e\u0432\u0430\u0440\u0438\u043c\u043e\u043c \u0432\u0438\u0434\u0435 type OptionsConfig = Exclude&lt;ParseArgsConfig[\"options\"], undefined>;  function getOptionsConfigFromMetadata(   Program: new () => any,   program: any, ): OptionsConfig {   const config: OptionsConfig = {};    for (const k of Object.keys(program)) {     const def = getOptionMetadata(Program, k);     if (!def) continue;      let short = def.short;     let type: \"string\" | \"boolean\" = \"string\";     let multiple = false;      if (def.type === String) {       type = \"string\";       multiple = false;     } else if (def.type === Boolean) {       type = \"boolean\";       multiple = false;     } else if (Array.isArray(def.type)) {       multiple = true;        if (def.type[0] === String) {         type = \"string\";       } else if (def.type[0] === Boolean) {         type = \"boolean\";       }     }      config[k] = { short, type, multiple };   }    return config; } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage5\/main.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { Command, Option, OptionValue, run } from \".\/framework.js\";  class Program {   @Option({ type: Boolean, short: \"v\" })   verbose = false;    version = \"1.0.0\";    @Command()   hello(opts: Record&lt;\"e\" | \"enthusiastic\", OptionValue>, name: string) {     if (this.verbose) {       console.debug(this);       console.debug([name]);       console.debug(opts);     }      const enthusiastic = opts.enthusiastic ?? opts.e ?? false;     if (typeof enthusiastic !== \"boolean\") {       console.error(`invalid type for --enthusiastic option`);       return 1;     }      console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);     return 0;   } }  await run(Program); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 6: \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0442\u0438\u043f\u044b \u0443 \u0441\u0430\u043c\u043e\u0433\u043e TypeScript<\/font><\/h2>\n<p>  \u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 TypeScript \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0435 \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0442\u044c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u0432 \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435, \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044f \u0438\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0432 TypeScript, \u0430 \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0443 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0442\u0438\u043f\u0430\u0445 \u0432 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\u043e\u043f\u0446\u0438\u044f \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430 <a href=\"https:\/\/www.typescriptlang.org\/tsconfig#emitDecoratorMetadata\">emitDecoratorMetadata<\/a> \u2014 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445;<\/li>\n<li>\u043e\u043f\u0446\u0438\u044f \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430 <a href=\"https:\/\/www.typescriptlang.org\/tsconfig#experimentalDecorators\">experimentalDecorators<\/a> \u2014 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0435\u0440\u0432\u043e\u0439 \u043e\u043f\u0446\u0438\u0438;<\/li>\n<li>\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/www.npmjs.com\/package\/reflect-metadata\">reflect-metadata<\/a> \u2014 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445.<\/li>\n<\/ul>\n<p>  \u041d\u0443\u0436\u043d\u044b \u0438\u043c\u0435\u043d\u043d\u043e \u00ab\u0441\u0442\u0430\u0440\u044b\u0435\u00bb \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b, \u0430 \u043d\u0435 \u043d\u043e\u0432\u044b\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435. \u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442, <code>emitDecoratorMetadata<\/code> <em>\u0442\u0440\u0435\u0431\u0443\u0435\u0442<\/em> <code>experimentalDecorators<\/code>.<\/p>\n<p>  \u041c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u043b\u0435\u043d\u043e\u0432 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043f\u0440\u0438\u0447\u0451\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0442\u0435\u0445, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0443\u0436\u0435 \u0432\u0438\u0441\u0438\u0442 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440. \u041a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0435 \u043e\u043f\u0438\u0441\u0430\u043d \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430, \u043d\u043e \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0435\u0441\u043b\u0438 <a href=\"https:\/\/www.typescriptlang.org\/play?experimentalDecorators=true&amp;emitDecoratorMetadata=true&amp;target=9#code\/CYUwxgNghgTiAEYD2A7AzgF3gWQJ4BFwkYoNiAueAMQFcUwMBLVAbgCg3Io01qkl4AbzbxR8AAJ5CyEmRgixoGaRDAACjCQAHAISVMMRigDm7BaLpLiK9Zt36Mhk2bESpRWcXPwrs1dhAMAAskYAAKADN+Sip+ABp4FEoUGgBbACMQGABKBydjIW9XOAwaGBR4ACJuYAjK9lcAXzZGoA\">\u043f\u043e\u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/a> \u0441 \u0442\u0435\u043c, \u0432\u043e \u0447\u0442\u043e \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  \u0421\u0430\u043c\u043e\u0435 \u0432\u0430\u0436\u043d\u043e\u0435, \u0447\u0442\u043e \u043e \u043d\u0451\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u043d\u0430\u0442\u044c \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u2014 \u043e\u043d \u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439, \u043a\u0430\u043a \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b. \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0433\u043e \u00ab\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u00bb, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043f\u043e\u043d\u044f\u0442\u0438\u0435 \u043a \u043d\u0435\u043c\u0443 \u0432\u043e\u043e\u0431\u0449\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c\u043e. \u0422\u043e \u0435\u0441\u0442\u044c: \u0434\u043b\u044f \u043a\u043b\u0430\u0441\u0441\u0430 <code>Foo<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d <code>Foo<\/code>, \u0434\u043b\u044f <code>number<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d <code>Number<\/code>, \u0434\u043b\u044f <code>number[]<\/code> \u2014 <code>Array<\/code>, \u0430 \u0434\u043b\u044f \u0442\u0438\u043f\u0430-\u043b\u0438\u0442\u0435\u0440\u0430\u043b\u0430 <code>{ name: string }<\/code> \u2014 \u043f\u0440\u043e\u0441\u0442\u043e <code>Object<\/code>. \u041e\u0431\u0438\u0434\u043d\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u0437\u0430 \u043c\u0430\u0441\u0441\u0438\u0432\u044b: \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0445\u043e\u0442\u044f \u0431\u044b \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438, \u043d\u043e \u0434\u043b\u044f \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u0432 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431 \u044f\u0432\u043d\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u0438\u043f \u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>  \u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0440\u0430\u0434\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0435\u0437 TypeScript, \u044d\u0442\u043e\u0442 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0442\u0438\u043f\u0430\u0445 \u043d\u0435\u043b\u044c\u0437\u044f \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u0430\u043a \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0435\u0441\u043b\u043e\u0436\u043d\u043e, \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0442\u0430\u0432\u0442\u043e\u043b\u043e\u0433\u0438\u0438 \u043e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage6\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import \"reflect-metadata\"; import { ParseArgsConfig, parseArgs } from \"node:util\";  export interface OptionDefinition {   short?: string;   type?: typeof String | typeof Boolean | [typeof String] | [typeof Boolean]; }  export const Option = (def: OptionDefinition) => Reflect.metadata(Option, def);  const getOptionMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Option, ctor.prototype, prop) as     | OptionDefinition     | undefined;  export const Command = () => Reflect.metadata(Command, {});  const getCommandMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Command, ctor.prototype, prop) as {} | undefined;  export type CommandFn = (   opts: Record&lt;string, OptionValue>,   ...args: string[] ) => void | number | Promise&lt;void | number>;  export type OptionValue =   | undefined   | boolean   | string   | Array&lt;boolean | string>;  export async function run(Program: new () => any) {   const program = new Program();    const {     positionals: [command, ...args],     values: opts,   } = parseArgs({     strict: false,     options: getOptionsConfigFromMetadata(Program, program),   });    for (const k of Object.keys(program)) {     const def = getOptionMetadata(Program, k);     if (!def) continue;      if (def.short &amp;&amp; def.short in opts) {       program[k] = opts[def.short];       delete opts[def.short];     }      if (k in opts) {       program[k] = opts[k];       delete opts[k];     }   }    const commands: string[] = [];    for (const k of Object.getOwnPropertyNames(Program.prototype)) {     const def = getCommandMetadata(Program, k);     if (!def) continue;      commands.push(k);   }    if (!command) {     console.error(\"no command specified\");     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    if (!commands.includes(command)) {     console.error(`unknown command: ${command}`);     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    const commandFn: Function = program[command];   const minArgCount = commandFn.length - 1;    if (args.length &lt; minArgCount) {     console.error(`too few arguments for command ${command}`);     console.error(`at least ${minArgCount}, ${args.length} given`);      process.exitCode = 1;     return;   }    try {     const code = await program[command]!(opts, ...args);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } }  type OptionsConfig = Exclude&lt;ParseArgsConfig[\"options\"], undefined>;  function getOptionsConfigFromMetadata(   Program: new () => any,   program: any, ): OptionsConfig {   const config: OptionsConfig = {};    for (const k of Object.keys(program)) {     const def = getOptionMetadata(Program, k);     if (!def) continue;      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0438\u0437 \u0442\u0438\u043f\u043e\u0432 TypeScript     const defType =       def.type ?? Reflect.getMetadata(\"design:type\", Program.prototype, k);      let short = def.short;     let type: \"string\" | \"boolean\" = \"string\";     let multiple = false;      if (defType === String) {       type = \"string\";       multiple = false;     } else if (defType === Boolean) {       type = \"boolean\";       multiple = false;     } else if (Array.isArray(defType)) {       multiple = true;        if (defType[0] === String) {         type = \"string\";       } else if (defType[0] === Boolean) {         type = \"boolean\";       }     } else {       throw new Error(`unable to determine option type for ${k}`);     }      config[k] = { short, type, multiple };   }    return config; } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage6\/main.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { Command, Option, OptionValue, run } from \".\/framework.js\";  class Program {   \/\/ \u0434\u043b\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b emitDecoratorMetadata   \/\/ \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u044f\u0432\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0442\u0438\u043f\u044b, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0432\u0435\u0441\u0442\u0438   @Option({ short: \"v\" })   verbose: boolean = false;    version = \"1.0.0\";    @Command()   hello(opts: Record&lt;\"e\" | \"enthusiastic\", OptionValue>, name: string) {     if (this.verbose) {       console.debug(this);       console.debug([name]);       console.debug(opts);     }      const enthusiastic = opts.enthusiastic ?? opts.e ?? false;     if (typeof enthusiastic !== \"boolean\") {       console.error(`invalid type for --enthusiastic option`);       return 1;     }      console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);     return 0;   } }  await run(Program); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0423\u0440\u043e\u0432\u0435\u043d\u044c 7: \u0442\u0438\u043f\u044b \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, DTO-\u043a\u043b\u0430\u0441\u0441\u044b<\/font><\/h2>\n<p>  \u0422\u0435\u043c \u0436\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u2014 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>emitDecoratorMetadata<\/code> \u2014 \u043c\u043e\u0436\u043d\u043e \u0443\u0437\u043d\u0430\u0442\u044c \u0438 \u0442\u0438\u043f\u044b \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u044d\u0442\u0438\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043a\u043e\u043d\u0435\u0446-\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0443 \u0441\u0430\u043c\u043e\u043c\u0443 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u0442\u0438\u043f\u044b \u043e\u043f\u0446\u0438\u0439 \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043a\u043e\u043c\u0430\u043d\u0434.<\/p>\n<p>  \u041f\u043e\u0434\u0432\u043e\u0445, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e, \u043a\u0430\u043a \u044f \u043f\u0438\u0441\u0430\u043b \u0432\u044b\u0448\u0435, \u0434\u043b\u044f \u0442\u0438\u043f\u043e\u0432-\u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f, \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u044d\u0442\u043e\u0442 \u0442\u0438\u043f \u2014 \u043a\u043b\u0430\u0441\u0441. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u043e\u0439\u0442\u0438 \u044d\u0442\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435, \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0442\u044c \u0442\u0438\u043f\u044b-\u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0438\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a <code>class<\/code>, \u0430 \u043d\u0435 \u043a\u0430\u043a <code>interface<\/code> \u0438\u043b\u0438 \u0442\u0438\u043f-\u043b\u0438\u0442\u0435\u0440\u0430\u043b.<\/p>\n<pre><code class=\"javascript\">\/\/ \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e: interface IOptions {   enthusiastic: boolean; }  \/\/ \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0442\u044c \u0442\u0430\u043a: class Options {   enthusiastic: boolean; }  \/\/ \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441, \/\/ \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0442\u0438\u043f\u043e\u0432 \u0442\u0430\u043a\u043e\u0435 \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u0442 const opts1: Options = { enthusiastic: true }; const opts2: Options = JSON.parse(str);  \/\/ \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u044d\u0442\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043d\u0435 \u0441\u0442\u0430\u043d\u0443\u0442 \u0432\u043e\u043b\u0448\u0435\u0431\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \/\/ \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 Options assert(!(opts1 instanceof Options)); <\/code><\/pre>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0442\u0440\u0438\u0445 \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0443, \u0447\u0442\u043e\u0431\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u043c\u0443 \u043a\u043e\u0434\u0443 \u0443\u0436\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c \u043e\u043f\u0446\u0438\u0438 \u0440\u0443\u043a\u0430\u043c\u0438:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">stage7\/framework.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import \"reflect-metadata\"; import { ParseArgsConfig, parseArgs } from \"node:util\";  export interface OptionDefinition {   short?: string;   type?: typeof String | typeof Boolean | [typeof String] | [typeof Boolean]; }  export const Option = (def: OptionDefinition) => Reflect.metadata(Option, def);  const getOptionMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Option, ctor.prototype, prop) as     | OptionDefinition     | undefined;  export const Command = () => Reflect.metadata(Command, {});  const getCommandMetadata = (ctor: new () => any, prop: string) =>   Reflect.getMetadata(Command, ctor.prototype, prop) as {} | undefined;  export type CommandFn = (   opts: Record&lt;string, OptionValue>,   ...args: string[] ) => void | number | Promise&lt;void | number>;  export type OptionValue =   | undefined   | boolean   | string   | Array&lt;boolean | string>;  export async function run(Program: new () => any) {   const program = new Program();    const {     positionals: [command],   } = parseArgs({     strict: false,     options: getOptionsConfigFromMetadata(Program, program),   });    const commands: string[] = [];    for (const k of Object.getOwnPropertyNames(Program.prototype)) {     const def = getCommandMetadata(Program, k);     if (!def) continue;      commands.push(k);   }    if (!command) {     console.error(\"no command specified\");     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    if (!commands.includes(command)) {     console.error(`unknown command: ${command}`);     console.error(`available commands: ${commands.join(\", \")}`);      process.exitCode = 1;     return;   }    const OptsDto = Reflect.getMetadata(     \"design:paramtypes\",     Program.prototype,     command,   )?.[0];    const optsDto = OptsDto ? new OptsDto() : undefined;    const {     positionals: [, ...args],     values: opts,   } = parseArgs({     strict: false,     options: {       ...getOptionsConfigFromMetadata(Program, program),       ...getOptionsConfigFromMetadata(OptsDto, optsDto),     },   });    Object.assign(optsDto, opts);    const commandFn: Function = program[command];   const minArgCount = commandFn.length - 1;    if (args.length &lt; minArgCount) {     console.error(`too few arguments for command ${command}`);     console.error(`at least ${minArgCount}, ${args.length} given`);      process.exitCode = 1;     return;   }    try {     const code = await program[command]!(optsDto, ...args);     process.exitCode = code ?? 0;   } catch (error: any) {     process.exitCode = error.exitCode ?? 1;     throw error;   } }  type OptionsConfig = Exclude&lt;ParseArgsConfig[\"options\"], undefined>;  function getOptionsConfigFromMetadata(   Program: new () => any,   program: any, ): OptionsConfig {   const config: OptionsConfig = {};    for (const k of Object.keys(program)) {     const def = getOptionMetadata(Program, k);     if (!def) continue;      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0438\u0437 \u0442\u0438\u043f\u043e\u0432 TypeScript     const defType =       def.type ?? Reflect.getMetadata(\"design:type\", Program.prototype, k);      let short = def.short;     let type: \"string\" | \"boolean\" = \"string\";     let multiple = false;      if (defType === String) {       type = \"string\";       multiple = false;     } else if (defType === Boolean) {       type = \"boolean\";       multiple = false;     } else if (Array.isArray(defType)) {       multiple = true;        if (defType[0] === String) {         type = \"string\";       } else if (defType[0] === Boolean) {         type = \"boolean\";       }     } else {       throw new Error(`unable to determine option type for ${k}`);     }      config[k] = { short, type, multiple };   }    return config; }  function extractOptions(   Dto: new () => any,   dto: any,   opts: Record&lt;string, OptionValue>, ): void {   for (const k of Object.keys(dto)) {     const def = getOptionMetadata(Dto, k);     if (!def) continue;      if (def.short &amp;&amp; def.short in opts) {       dto[k] = opts[def.short];       delete opts[def.short];     }      if (k in opts) {       dto[k] = opts[k];       delete opts[k];     }   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0439 \u043a\u043e\u0434 \u0438\u0437 <code>main.ts<\/code>:<\/p>\n<pre><code class=\"javascript\">import { Command, Option, run } from \".\/framework.js\";  \/\/ \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u043a\u0430\u043a abstract class HelloOptions {   @Option({ short: \"e\" })   enthusiastic: boolean = false; }  class Program {   @Option({ short: \"v\" })   verbose: boolean = false;    version = \"1.0.0\";    @Command()   hello({ enthusiastic }: HelloOptions, name: string) {     if (this.verbose) {       console.debug(this);       console.debug([name]);       console.debug({ enthusiastic });     }      console.log(`Hello ${name}${enthusiastic ? \"!\" : \".\"}`);     return 0;   } }  await run(Program); <\/code><\/pre>\n<p>  <\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0427\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435?<\/font><\/h2>\n<p>  \u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e \u0438\u0437 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u043d\u0430\u043c \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043f\u0440\u044f\u0442\u0430\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0432\u0435\u0441\u044c \u043a\u043e\u0434, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0439 \u0441 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438. \u041a\u043b\u0438\u0435\u043d\u0442\u0443 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043a\u043e\u0434 \u0432 \u043a\u043b\u0430\u0441\u0441\u044b, \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044f\u0441\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0439, \u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0443\u0436\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u0432\u0441\u0451 \u0441\u0430\u043c.<\/p>\n<p>  \u0421\u0445\u043e\u0436\u0438\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u044b \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u0438 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0448\u0438\u0440\u043e\u043a\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0432\u043e \u043c\u043d\u043e\u0433\u0438\u0445 TypeScript-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445. \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0435\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0431\u044b\u043b, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, <a href=\"https:\/\/docs.nestjs.com\/\">Nest.js<\/a>. \u041d\u043e \u044f \u0441\u0447\u0438\u0442\u0430\u044e, \u0447\u0442\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0432\u044b\u0431\u043e\u0440\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0437\u043d\u0430\u043d\u0438\u0435 \u044d\u0442\u0438\u0445 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0435, \u043b\u0430\u043a\u043e\u043d\u0438\u0447\u043d\u044b\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0435 API.<\/p>\n<blockquote><p><b><font color=\"#3AC1EF\"><a href=\"https:\/\/t.me\/ruvds_community\">Telegram-\u043a\u0430\u043d\u0430\u043b \u0441 \u0440\u043e\u0437\u044b\u0433\u0440\u044b\u0448\u0430\u043c\u0438 \u043f\u0440\u0438\u0437\u043e\u0432, \u043d\u043e\u0432\u043e\u0441\u0442\u044f\u043c\u0438 IT \u0438 \u043f\u043e\u0441\u0442\u0430\u043c\u0438 \u043e \u0440\u0435\u0442\u0440\u043e\u0438\u0433\u0440\u0430\u0445 ?\ufe0f<\/a><\/font><\/b><\/p><\/blockquote>\n<p><a href=\"http:\/\/ruvds.com\/ru-rub?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=iliazeus&amp;utm_content=refleksiya_v_javascript_i_typescript:_obzor_osnovnyx_texnik._kak_sgenerirovat_dlya_klassa_cli-interfejs\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/_p\/_h\/lv\/_p_hlvd2tv0cv9tny8tdytzfhje.png\" data-src=\"https:\/\/habrastorage.org\/webt\/_p\/_h\/lv\/_p_hlvd2tv0cv9tny8tdytzfhje.png\"\/><\/a><\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \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\/754764\/\"> https:\/\/habr.com\/ru\/articles\/754764\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\u041a\u0430\u043a \u0438 \u0432 \u043b\u044e\u0431\u043e\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435, \u0432 JavaScript \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0435\u0441\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u2014 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0438\u043f\u044b, \u043a\u043b\u044e\u0447\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u044b \u0438 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u044b.<\/p>\n<p>  \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0442\u0430\u043a\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438, \u043f\u043b\u044e\u0441 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0435\u0449\u0451 \u0431\u043e\u043b\u044c\u0448\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0442\u0438\u043f\u0430\u0445 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 TypeScript, \u0438 \u043a\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u0430\u043c \u0438 \u0438\u0445 \u043f\u043e\u043b\u044f\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432. \u041a\u0430\u0436\u0434\u0443\u044e \u0438\u0437 \u0442\u0435\u0445\u043d\u0438\u043a \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e CLI-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043a \u043a\u043e\u043d\u0446\u0443 \u0441\u0442\u0430\u0442\u044c\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u0430\u043a \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435:<\/p>\n<p>  <a href=\"https:\/\/habr.com\/ru\/companies\/ruvds\/articles\/754764\/\"><\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/te\/d6\/lq\/ted6lqrchhdoezwwrmw9cxpsmjy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/te\/d6\/lq\/ted6lqrchhdoezwwrmw9cxpsmjy.png\"\/><\/div>\n<p><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-352232","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/352232","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=352232"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/352232\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=352232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=352232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=352232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}