{"id":362140,"date":"2024-05-21T01:28:36","date_gmt":"2024-05-21T01:28:36","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=362140"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=362140","title":{"rendered":"<span>Frontend Talks: \u0443\u0441\u0438\u043b\u0438\u0432\u0430\u0435\u043c TypeScript \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e switch + notReachable<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u042f \u0418\u043b\u044c\u044f \u0411\u0435\u043b\u044f\u0432\u0441\u043a\u0438\u0439 \u2014 \u0441\u0442\u0430\u0440\u0448\u0438\u0439 Frontend-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <a href=\"https:\/\/cloud.ru\/ru?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=frontendtalks_switch_notreachable_08122023\">\u0432 Cloud.ru<\/a>. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043d\u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 TypeScript, \u043f\u043e\u0432\u044b\u0441\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043a\u043e\u0434\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u044b \u0438 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0445\u0435\u043b\u043f\u0435\u0440\u0430 \u2014 notReachable. \u0415\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0437\u0430\u0433\u043b\u044f\u0434\u044b\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434 \u043a\u0430\u0442!<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/1u\/el\/dh\/1ueldh1ijaj2ihcnqomdm8wzqnu.png\" data-src=\"https:\/\/habrastorage.org\/webt\/1u\/el\/dh\/1ueldh1ijaj2ihcnqomdm8wzqnu.png\"\/><\/figure>\n<p>\u041f\u0440\u043e \u0447\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/p>\n<p><a href=\"#aaa\">\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 switch + notReachable<\/a><br \/> <a href=\"#bbb\">\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u0441\u0442\u0438\u043b\u0435 making impossible states impossible<\/a><br \/> <a href=\"#ccc\">\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 onMsg<\/a><br \/> <a href=\"#ddd\">\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 Layout + Modal<\/a><\/p>\n<p>\u0418\u0442\u0430\u043a, \u043d\u0430\u0447\u043d\u0435\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<p><a class=\"anchor\" name=\"aaa\" id=\"aaa\"><\/a><\/p>\n<h2>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 switch + notReachable<\/h2>\n<p>notReachable \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043d\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0445\u0435\u043b\u043f\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0435\u0449\u0435 \u043c\u043e\u0449\u043d\u0435\u0435 \u0432 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0438 \u0441\u043e switch. \u0420\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043f\u043e \u043a\u0430\u043a\u043e\u043c\u0443 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0440\u0430\u0432\u043d\u0438\u043c: \u0441\u0432\u0435\u0440\u0445\u0443 <code>assertNever<\/code>(\u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043f\u043e TypeScript), \u043f\u043e\u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0435 <code>notReachable<\/code>, \u0430 \u0432\u043d\u0438\u0437\u0443 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0431\u0435\u0437 \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<pre><code class=\"typescript\">function assertNever(x: never): never {   throw new Error(`Unexpected object: ` + x); }  function notReachable(_: never): never {   throw new Error(`Should never be reached ${_}`); }  const notReachable = (_: never): never => _; <\/code><\/pre>\n<p>\u0421\u0443\u0442\u044c \u0443 \u043d\u0438\u0445 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u0430\u044f: \u043d\u0430 \u0432\u0445\u043e\u0434\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0441 \u0442\u0438\u043f\u043e\u043c <code>never<\/code>. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c <code>never<\/code>\u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0438 \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435. \u041a\u0430\u043a \u0442\u0430\u043a\u043e\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e? \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u0445.<\/p>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440 1. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u043e\u043f\u043b\u0430\u0442\u044b \u0447\u0435\u0440\u0435\u0437 Union<\/h3>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0443 \u043d\u0430\u0441 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0435\u0441\u0442\u044c \u0442\u0438\u043f <code>PaymentMethod<\/code> \u2014 \u043e\u043f\u043b\u0430\u0442\u0430 \u043f\u043e \u043a\u0430\u0440\u0442\u0435, \u043b\u0438\u0431\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0432 \u0431\u0430\u043d\u043a. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0441\u0435 \u0442\u0438\u043f\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c\u0438, \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u2014 \u043e\u0431\u0449\u0435\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>type<\/code>, \u0431\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0449\u0435\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043c\u043e\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043b\u044e\u0431\u043e\u0435, \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u2014 \u043e\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0432\u043e \u0432\u0441\u0435\u0445 \u0442\u0438\u043f\u0430\u0445 Union.<\/p>\n<p>\u042d\u0442\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0430\u043d\u0442\u043e\u0439, \u0430 \u0441\u0430\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u2014 <strong>Discriminated Unions<\/strong> (\u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0430\u043d\u0442\u043d\u043e\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435). \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043d\u0435\u043c\u0443 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 TypeScript \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u044c \u0442\u0438\u043f\u044b \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u0449\u0435\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442.<\/p>\n<pre><code class=\"typescript\">type Card = {   type: 'card';   cardNumber: string;   cardHolder: string;   expiryDate: string;   cvcNumber: number; };  type BankTransfer = {   type: 'bankTransfer';   bankName: string;   accountNumber: string; };  type PaymentMethod = Card | BankTransfer;  type Props = {   paymentMethod: PaymentMethod; };  export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>           &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>         &lt;\/S.Wrapper>       );      default:       const _exhaustiveCheck: never = paymentMethod;       return _exhaustiveCheck;   } } <\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0430\u0448\u0438\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0430\u043d\u0442\u0430 type \u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f switch. TypeScript \u00ab\u043f\u043e\u0439\u043c\u0435\u0442\u00bb, \u0447\u0442\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>cardNumber<\/code>, <code>cardHolder<\/code>, <code>expiryDate<\/code> \u0438 <code>cvcNumber<\/code> \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043a \u0442\u0438\u043f\u0443 <code>card<\/code> \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0442\u0438\u043f\u0430 <code>card<\/code>. \u0415\u0441\u043b\u0438 \u0443 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>type<\/code> \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>card<\/code>, \u043c\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c <code>accountNumber<\/code> \u0438 <code>bankName<\/code>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 default case \u0443\u043a\u0430\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>_exhaustiveCheck<\/code> (\u0438\u0441\u0447\u0435\u0440\u043f\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438) \u0441 \u0442\u0438\u043f\u043e\u043c <code>never<\/code> \u0438 \u043f\u0440\u0438\u0441\u0432\u043e\u0438\u043c \u0435\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>paymentMethod<\/code>. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0441\u0451 \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u0435\u0437 \u043e\u0448\u0438\u0431\u043e\u043a: \u043c\u044b \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u043b\u0438 \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0432 switch, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 default case \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 (never \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e paymentMethod).<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043d\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e, \u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430 <code>notReachable<\/code>. \u0412 \u0447\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u0430 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0443\u0432\u0438\u0434\u0438\u043c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<pre><code class=\"typescript\">function notReachable(_: never): never {   throw new Error(`Should never be reached ${_}`); }  export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>           &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>         &lt;\/S.Wrapper>       );      default:       return notReachable(paymentMethod);   } } <\/code><\/pre>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0442\u0438\u043f\u0430\u043c \u043e\u043f\u043b\u0430\u0442\u044b \u0435\u0449\u0435 \u043e\u0434\u0438\u043d. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0431\u044b\u0441\u0442\u0440\u044b\u0445 \u043f\u043b\u0430\u0442\u0435\u0436\u0435\u0439 (SBP). \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f <code>SBP<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 Union <code>PaymentMethod<\/code>.<\/p>\n<pre><code class=\"typescript\">type Card = {   type: 'card';   cardNumber: string;   cardHolder: string;   expiryDate: string;   cvcNumber: number; };  type BankTransfer = {   type: 'bankTransfer';   bankName: string;   accountNumber: string; };  type SBP = {   type: 'sbp';   phoneNumber: number; };  export type PaymentMethod = Card | BankTransfer | SBP; <\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u043c, \u0441\u0440\u0430\u0437\u0443 \u0443\u0432\u0438\u0434\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430 TypeScript \u0432\u043e \u0432\u0441\u0435\u0445 \u043c\u0435\u0441\u0442\u0430\u0445, \u0433\u0434\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e switch + notReachable:<\/p>\n<pre><code class=\"typescript\">ERROR in .\/src\/components\/CheckoutForm\/CheckoutForm.tsx:16:27 TS2345: Argument of type 'SBP' is not assignable to parameter of type 'never'.     14 |     15 |     default:   > 16 |       return notReachable(paymentMethod);        |                           ^^^^^^^^^^^^^     17 |   }     18 | };     19 |  ERROR in .\/src\/components\/PaymentSection\/PaymentSection.tsx:31:27 TS2345: Argument of type 'SBP' is not assignable to parameter of type 'never'.     29 |     30 |     default:   > 31 |       return notReachable(paymentMethod);        |                           ^^^^^^^^^^^^^     32 |   }     33 | }     34 | <\/code><\/pre>\n<p>\u041d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f <code>SBP<\/code> \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>notReachable<\/code> \u0441 \u0442\u0438\u043f\u043e\u043c <code>never<\/code>. \u041d\u043e \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f notReachable \u043f\u0440\u0438 \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043a\u0430\u0442\u044c \u0432\u0441\u0435 \u043c\u0435\u0441\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u2014 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 TypeScript \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u044d\u0442\u043e \u0437\u0430 \u043d\u0430\u0441.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/zv\/qg\/vf\/zvqgvfzinzy0hb2s_176jkqaqnk.png\" data-src=\"https:\/\/habrastorage.org\/webt\/zv\/qg\/vf\/zvqgvfzinzy0hb2s_176jkqaqnk.png\"\/><\/figure>\n<p>\u0427\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f <code>SBP<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435:<\/p>\n<pre><code class=\"typescript\">export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'sbp':       return (         &lt;S.Wrapper>           &lt;p>Phone number: {paymentMethod.phoneNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>           &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>         &lt;\/S.Wrapper>       );      default:       return notReachable(paymentMethod);   } } <\/code><\/pre>\n<p>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u0442, \u0435\u0441\u043b\u0438 \u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a \u0432\u043d\u043e\u0432\u044c \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u0442 \u043d\u0430\u0441 \u0432\u043d\u0435\u0441\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443\u0431\u0440\u0430\u0442\u044c <code>SBP<\/code> \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u043e\u043f\u043b\u0430\u0442\u044b?<\/p>\n<pre><code class=\"typescript\">type Card = {   type: 'card';   cardNumber: string;   cardHolder: string;   expiryDate: string;   cvcNumber: number; };  type BankTransfer = {   type: 'bankTransfer';   bankName: string;   accountNumber: string; };  export type PaymentMethod = Card | BankTransfer; <\/code><\/pre>\n<p>\u0412\u0435\u0440\u043d\u043e, \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432\u043d\u043e\u0432\u044c \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u0438\u0442 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0432\u0435\u0441\u044c \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0440\u0430\u0437\u0443 \u0443\u0434\u0430\u043b\u0438\u0442\u044c:<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/cb\/fh\/gb\/cbfhgbw2r2h5p7xqjhv9h4e8gyq.png\" data-src=\"https:\/\/habrastorage.org\/webt\/cb\/fh\/gb\/cbfhgbw2r2h5p7xqjhv9h4e8gyq.png\"\/><\/figure>\n<pre><code class=\"typescript\">ERROR in .\/src\/components\/PaymentSection\/PaymentSection.tsx:12:10 TS2678: Type '\"sbp\"' is not comparable to type '\"card\" | \"bankTransfer\"'.     10 | export function PaymentSection({ paymentMethod }: Props) {     11 |   switch (paymentMethod.type) {   > 12 |     case 'sbp':        |          ^^^^^     13 |       return (     14 |         &lt;S.Wrapper>     15 |           &lt;p>Phone number: {paymentMethod.phoneNumber}&lt;\/p>  ERROR in .\/src\/components\/PaymentSection\/PaymentSection.tsx:15:43 TS2339: Property 'phoneNumber' does not exist on type 'never'.     13 |       return (     14 |         &lt;S.Wrapper>   > 15 |           &lt;p>Phone number: {paymentMethod.phoneNumber}&lt;\/p>        |                                           ^^^^^^^^^^^     16 |         &lt;\/S.Wrapper>     17 |       );     18 | <\/code><\/pre>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u0438\u0442\u0435\u0441\u044c \u0443\u0434\u043e\u0431\u043d\u043e? \u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440.<\/p>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440 2. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0432 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 IDE<\/h3>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435, \u043a\u0430\u043a \u0443\u0434\u043e\u0431\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 IDE:<\/p>\n<ul>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0443\u0441\u0442\u043e\u0439 switch,<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u043a\u0443\u0440\u0441\u043e\u0440 \u043d\u0430 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0441\u043b\u043e\u0432\u043e switch,<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u0436\u043c\u0435\u043c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e Alt + Enter.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u043e\u0442 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u0435\u0439\u0441\u044b \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0432 \u043f\u0430\u0440\u0443 \u043a\u043b\u0438\u043a\u043e\u0432. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0445\u0435\u043b\u043f\u0435\u0440 <code>notReachable<\/code> \u0432 default case.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/3k\/28\/fb\/3k28fbqgbmr5pu7xgw8s3r4k-ni.gif\" data-src=\"https:\/\/habrastorage.org\/webt\/3k\/28\/fb\/3k28fbqgbmr5pu7xgw8s3r4k-ni.gif\"\/><\/figure>\n<p>\u0418\u0437-\u0437\u0430 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 switch \u043c\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043c\u043d\u0438\u0442\u044c \u043f\u0440\u043e \u043b\u043e\u0433\u0438\u043a\u0443 default case. \u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>PaymentSection<\/code> \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0435\u0437 switch \u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u044b\u0445 if, \u043d\u043e \u0443 \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0438 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438.<\/p>\n<pre><code class=\"typescript\">export function PaymentSection({ paymentMethod }: Props) {   if (paymentMethod.type === 'card') {     return (       &lt;S.Wrapper>         &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>         &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>         &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>         &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>       &lt;\/S.Wrapper>     );   }    if (paymentMethod.type === 'bankTransfer') {     return (       &lt;S.Wrapper>         &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>         &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>       &lt;\/S.Wrapper>     );   }    return notReachable(paymentMethod); } <\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c If, \u0442\u043e \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u0440\u043e <code>notReachable<\/code>, \u0430 \u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c switch \u2014 \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u044f\u0437\u0430\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c default case. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0445\u0435\u043b\u043f\u0435\u0440 \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u0438\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u0438 \u043b\u044e\u0431\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 Union \u0438 \u043c\u044b \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u0435\u043c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<pre><code class=\"typescript\">export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>           &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>         &lt;\/S.Wrapper>       );      default:       return notReachable(paymentMethod);   } } <\/code><\/pre>\n<h3>\u0412\u044b\u0432\u043e\u0434\u044b<\/h3>\n<p>\u0412 \u0447\u0451\u043c \u043f\u043e\u043b\u044c\u0437\u0430 switch + notReachable:<\/p>\n<p><a class=\"anchor\" name=\"bbb\" id=\"bbb\"><\/a><\/p>\n<ul>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u0432\u044b \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u0442\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 Union \u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 \u043d\u043e\u0432\u043e\u0435, \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u0438\u0442 \u0432\u0441\u0435 \u043c\u0435\u0441\u0442\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0443\u0436\u043d\u043e \u0432\u043d\u0435\u0441\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043d\u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 TypeScript;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0449\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u043a\u043e\u0434\u043e\u0432\u0443\u044e \u0431\u0430\u0437\u0443;<\/p>\n<\/li>\n<li>\n<p>\u0432\u043e\u0437\u0440\u0430\u0441\u0442\u0430\u0435\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<h2>\u0422\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u0441\u0442\u0438\u043b\u0435 making impossible states impossible<\/h2>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0442\u0438\u043f <code>Connector<\/code> \u2014 \u043e\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0443, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0440\u0430\u0441\u043f\u043e\u043b\u0430\u0433\u0430\u044e\u0442\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<pre><code class=\"typescript\">export type Connector = {   bucket?: string;   endpoint?: string;   access_key_id?: string;   security_key?: string; }; <\/code><\/pre>\n<p>\u041a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u2014 \u043a\u0430\u0436\u0434\u043e\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c. \u041e\u0434\u043d\u0430\u043a\u043e \u0435\u0441\u0442\u044c \u043d\u044e\u0430\u043d\u0441 \u2014 \u044d\u0442\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0440\u0430\u0437\u0443 16 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432!<\/p>\n<pre><code class=\"typescript\">const validSamples: Connector[] = [   {},   { bucket: 'xxx'},   { endpoint: 'xxx' },    { access_key_id: 'xxx' },   { security_key: 'xxx' },   { bucket: 'xxx', endpoint: 'xxx' },   { bucket: 'xxx', access_key_id: 'xxx' },   { bucket: 'xxx', security_key: 'xxx' },   { endpoint: 'xxx', access_key_id: 'xxx' },   { endpoint: 'xxx', security_key: 'xxx' },   { access_key_id: 'xxx', security_key: 'xxx' },   { bucket: 'xxx', endpoint: 'xxx', access_key_id: 'xxx' },   { bucket: 'xxx', endpoint: 'xxx', security_key: 'xxx' },   { bucket: 'xxx', access_key_id: 'xxx', security_key: 'xxx' },   { endpoint: 'xxx', access_key_id: 'xxx', security_key: 'xxx' },   {      bucket: 'xxx',      endpoint: 'xxx',     security_key: 'xxx',     access_key_id: 'xxx',   } ] <\/code><\/pre>\n<p>\u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c\u0438 \u0431\u0443\u0434\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0430 \u0438\u0437 \u043d\u0438\u0445: \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 bucket \u0438\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e bucket&#8217;\u0430 \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u0414\u0432\u0435 \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0438 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043f\u0440\u043e\u0449\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c, \u0447\u0435\u043c 16, \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0442\u0435\u0441\u044c?<\/p>\n<pre><code class=\"typescript\">export type Connector =   | {       bucket: string;     }   | {       endpoint: string;       access_key_id: string;       security_key: string;     }; <\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u044d\u0442\u0438 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0432 \u043e\u0431\u0449\u0435\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>type<\/code>, \u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0439 \u043d\u0430\u043c Discriminated Unions. \u0422\u0430\u043a \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0438\u043f <code>Connector<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c switch + notReachable, \u0432\u043d\u043e\u0432\u044c \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043c \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043d\u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0438 \u0432\u043e\u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"typescript\">export type Connector =   | {       type: 'existing_connector',       bucket: string;     }   | {       type: 'new_connector',       endpoint: string;       access_key_id: string;       security_key: string;     }; <\/code><\/pre>\n<h3>\u0412\u044b\u0432\u043e\u0434\u044b<\/h3>\n<p><a class=\"anchor\" name=\"ccc\" id=\"ccc\"><\/a><\/p>\n<ul>\n<li>\n<p>\u041f\u043b\u043e\u0445\u043e \u043f\u0440\u043e\u0434\u0443\u043c\u0430\u043d\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0441\u043b\u043e\u0436\u043d\u043e \u0438 \u0431\u0435\u0441\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c: \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0431\u0430\u0433\u0430\u043c \u0438 \u043e\u0448\u0438\u0431\u043a\u0430\u043c.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043f\u043b\u043e\u0445\u043e \u043f\u0440\u043e\u0434\u0443\u043c\u0430\u043d\u043d\u044b\u0445 \u0442\u0438\u043f\u0430\u0445 \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435: \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e coverage \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<h2>\u041f\u043e\u0434\u0445\u043e\u0434 onMsg<\/h2>\n<p>onMsg \u2014 \u043f\u043e\u0434\u0445\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u043f\u0440\u043e\u043f\u0430\u043c\u0438-\u0445\u0435\u043d\u0434\u043b\u0435\u0440\u0430\u043c\u0438 \u043c\u0435\u0436\u0434\u0443 \u00ab\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c\u00bb \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u0438 \u00ab\u043f\u043e\u0442\u043e\u043c\u043a\u0430\u043c\u0438\u00bb.<\/p>\n<p>\u041a\u0430\u043a \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442? \u041c\u044b \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0435\u043c \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u00ab\u0440\u0435\u0431\u0435\u043d\u043a\u0430\u00bb \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u043d\u0443 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>onMsg<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0433\u043e (\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u0435\u0433\u043e \u043f\u043e\u0442\u043e\u043c\u043a\u043e\u0432) \u0441\u043e\u0431\u044b\u0442\u0438\u044f. \u0422. \u0435. \u041e\u0434\u0438\u043d callback <code>onMsg<\/code> \u0437\u0430\u043c\u0435\u043d\u0438\u0442 \u043b\u044e\u0431\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u043f\u043e\u0432-\u0445\u0435\u043d\u0434\u043b\u0435\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u043a\u0438\u043d\u0443\u0442\u044c.<\/p>\n<p>\u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0442\u0438\u043f <code>Msg<\/code> \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0441\u0430\u043c\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u00ab\u0440\u0435\u0431\u0435\u043d\u043a\u0430\u00bb.<\/p>\n<pre><code class=\"typescript\">\/\/ Child component export type Msg =   | {       type: 'chat_title_changed';       updatedTitle: string;     }   | {       type: 'chat_deleted';     };  type Props = {   chat: Chat;   onMsg: (msg: Msg) => void; }; <\/code><\/pre>\n<p>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0442\u0438\u043f\u0430 Msg:<\/p>\n<ol>\n<li>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0432 \u043f\u0440\u043e\u0448\u0435\u0434\u0448\u0435\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441 \u00ab\u0427\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e?\u00bb. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>chat_deleted<\/code>, <code>cancel_button_clicked<\/code>, <code>form_submitted<\/code> \u0438 \u0442. \u0434. \u042d\u0442\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043c\u044b \u043a\u0430\u043a \u0431\u0443\u0434\u0442\u043e \u0434\u0438\u0441\u043f\u0430\u0442\u0447\u0438\u043c: \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>onMsg<\/code> \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0434\u043d\u043e\u043c\u0443 \u0438\u0437 \u0442\u0438\u043f\u043e\u0432 Union <code>Msg<\/code>.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"typescript\">export type Msg =   | {       type: 'chat_title_changed';       updatedTitle: string;     }   | {       type: 'chat_deleted';     }   | {       type: 'cancel_button_clicked';     };  export function EditChatItem({ chat, onMsg }: Props) {   const [title, setTitle] = useState(chat.title);    const handleSubmit = () => {     if (!title.length) {       return;     }      onMsg({       type: 'chat_title_changed',       updatedTitle: title,     });   };    return (     &lt;S.Wrapper>       &lt;form         onSubmit={e => {           e.preventDefault();           handleSubmit();         }}       >         &lt;InputCommon value={title} onChange={setTitle} size={InputCommon.sizes.Small} \/>       &lt;\/form>        &lt;S.ActionButtons>         &lt;ButtonIcon icon={&lt;SaveInterfaceSVG \/>} onClick={handleSubmit} \/>         &lt;ButtonIcon icon={&lt;DeleteInterfaceSVG \/>} onClick={() => onMsg({ type: 'chat_deleted' })} \/>         &lt;ButtonIcon           icon={&lt;CircleCancelFilledInterfaceSVG \/>}           onClick={() => onMsg({ type: 'cancel_button_clicked' })}         \/>       &lt;\/S.ActionButtons>     &lt;\/S.Wrapper>   ); } <\/code><\/pre>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u0435\u0435 \u043d\u0430 \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u00ab\u0440\u0435\u0431\u0435\u043d\u043a\u0430\u00bb: \u0432 \u043a\u043e\u0434\u0435 \u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0432 <code>handleSubmit<\/code> \u0434\u0438\u0441\u043f\u0430\u0442\u0447\u0430\u0442\u0441\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f <code>chat_title_changed<\/code>. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>updatedTitle<\/code> \u2014 \u044d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0432 \u00ab\u043f\u0440\u0430\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u0445\u00bb \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f <code>chat_title_changed<\/code>, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0432 <code>chat_deleted<\/code> \u0438 <code>cancel_button_clicked<\/code> \u0438\u0445 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442. \u0418 \u0432\u0441\u0451 \u044d\u0442\u043e \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f Discriminated Unions.<\/p>\n<ol start=\"2\">\n<li>\n<p>\u0421\u043e\u0431\u044b\u0442\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0432\u044b\u0448\u0435 \u2014 \u0432 \u00ab\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u00bb \u0438\u043b\u0438 \u0432 \u00ab\u043f\u0440\u0430\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u0445\u00bb. \u042d\u0442\u043e \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u0430\u044f \u043d\u0430\u043c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f switch + notReachable, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432\u0441\u0435\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0435\u0434\u0448\u0438\u0445 \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 <code>EditChatItem<\/code>.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"typescript\">&lt;EditChatItem   chat={chat}   onMsg={msg => {     switch (msg.type) {       case 'chat_deleted':         deleteChat();         break;              case 'chat_title_changed':         updateChat({ title: msg.updatedTitle });         break;        case 'cancel_button_clicked':         setIsEdited(false);        default:         notReachable(msg);     }   }} \/> <\/code><\/pre>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f notReachable \u043b\u044e\u0431\u044b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430 <code>Msg<\/code> \u0432\u044b\u0437\u043e\u0432\u0443\u0442 \u043e\u0448\u0438\u0431\u043a\u0443. \u0415\u0441\u043b\u0438 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f <code>some_new_action_happened<\/code>, \u0443 \u043d\u0430\u0441 \u0441\u0440\u0430\u0437\u0443 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430 \u0438 \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043a\u0435\u0439\u0441.<\/p>\n<pre><code class=\"typescript\">export type Msg =   | {       type: 'chat_title_changed';       updatedTitle: string;     }   | {       type: 'chat_deleted';     }   | {       type: 'cancel_button_clicked';     }   | {       type: 'some_new_action_happened';     } <\/code><\/pre>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/e7\/o8\/qg\/e7o8qg9thnmxzsf7znfeothztcg.png\" data-src=\"https:\/\/habrastorage.org\/webt\/e7\/o8\/qg\/e7o8qg9thnmxzsf7znfeothztcg.png\"\/><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u0440\u0430\u0432\u043d\u0438\u043c. \u0412\u043e\u0442 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043a\u0430\u0436\u0434\u044b\u0439 callback \u2014\u00a0\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u043f:<\/p>\n<pre><code class=\"typescript\">type Props = {   chat: Chat;   onChatTitleChanged: (newTitle: string) => void;   onCancel: () => void;   onDelete: () => void; };  &lt;EditChatItem   chat={chat}   onChatTitleChanged={updatedTitle => updateChat({ title: updatedTitle })}   onDelete={deleteChat}   onCancel={() => setIsEdited(false)} \/> <\/code><\/pre>\n<p>\u0410 \u044d\u0442\u043e \u043f\u043e\u0434\u0445\u043e\u0434 \u0441 \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u043e\u043f-\u0445\u044d\u043d\u0434\u043b\u0435\u0440\u043e\u0432:<\/p>\n<pre><code class=\"typescript\">export type Msg =   | {       type: 'chat_title_changed';       updatedTitle: string;     }   | {       type: 'chat_deleted';     }   | {       type: 'cancel_button_clicked';     };  type Props = {   chat: Chat;   onMsg: (msg: Msg) => void; };  &lt;EditChatItem   chat={chat}   onMsg={msg => {     switch (msg.type) {       case 'chat_deleted':         deleteChat();         break;       case 'chat_title_changed':         updateChat({ title: msg.updatedTitle });         break;       case 'cancel_button_clicked':         setIsEdited(false);         break;        default:         notReachable(msg);     }   }} \/> <\/code><\/pre>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0435\u0435, \u043d\u043e \u0432\u043f\u043e\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u0438 \u043e\u043d \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043c\u0435\u043d\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0432 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0435. \u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u043c\u043e\u0436\u0435\u0442 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0439 <code>Msg<\/code>, \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0438\u0437 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043a\u0430\u043a \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0443, \u0442\u0430\u043a \u0438 \u043a\u043e \u0432\u0441\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u00ab\u043f\u043e\u0442\u043e\u043c\u043a\u043e\u0432\u00bb. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u00ab\u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c\u00bb \u0442\u0438\u043f <code>Msg<\/code> \u043d\u0430 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u00ab\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439\u00bb \u0438 \u00ab\u043f\u0440\u0430\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439\u00bb. \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435.<\/p>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0447\u0435\u0442\u044b\u0440\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430:<\/p>\n<ul>\n<li>\n<p>\u00ab\u043f\u0440\u0430\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u00bb \u2014 A,<\/p>\n<\/li>\n<li>\n<p>\u00ab\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u00bb \u2014 B,<\/p>\n<\/li>\n<li>\n<p>\u0434\u0432\u0430 \u00ab\u0440\u0435\u0431\u0435\u043d\u043a\u0430\u00bb \u2014 \u0421 \u0438 D.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/cv\/kr\/z7\/cvkrz7hox95y6-zxp8mymed5x3g.png\" data-src=\"https:\/\/habrastorage.org\/webt\/cv\/kr\/z7\/cvkrz7hox95y6-zxp8mymed5x3g.png\"\/><\/figure>\n<p>\u0412 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 B \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0442\u0438\u043f\u044b <code>Msg<\/code> \u0432\u0441\u0435\u0445 \u00ab\u0434\u0435\u0442\u0435\u0439\u00bb: \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 C \u2014 <code>PluginsTable<\/code> \u0438 D \u2014 <code>UrisTable<\/code>. \u042d\u0442\u0438 \u0442\u0438\u043f\u044b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u044e\u0442\u0441\u044f \u0441 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0442\u0438\u043f\u043e\u043c <code>Msg<\/code>, \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u043c \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 B. \u0417\u0430\u0442\u0435\u043c \u044d\u0442\u043e\u0442 \u0442\u0438\u043f \u0431\u0443\u0434\u0435\u0442 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d, \u0430 \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u044b \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0410.<\/p>\n<pre><code class=\"typescript\">\/\/ B import { Msg as PluginsTableMsg, PluginsTable } from '#components\/PluginsTable'; \/\/ C import { Msg as UrisTableMsg, UrisTable } from '#components\/UrisTable'; \/\/ D  \/\/ \u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0432 A export type Msg =   | { type: 'form_submitted'; data: RuleInput }   | { type: 'close' }   | { type: 'add_plugin_button_clicked' }   | { type: 'add_uri_button_clicked' }   | PluginsTableMsg   | UrisTableMsg; <\/code><\/pre>\n<h3>\u0412\u044b\u0432\u043e\u0434\u044b<\/h3>\n<p><a class=\"anchor\" name=\"ddd\" id=\"ddd\"><\/a><\/p>\n<ul>\n<li>\n<p><code>onMsg<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0433\u0438\u0431\u043a\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0430 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0438 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c \u0432\u044b\u0448\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0441\u0438\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: \u0447\u0442\u043e\u0431\u044b \u043d\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>Msg<\/code> \u043f\u043e\u044f\u0432\u0438\u043b\u043e\u0441\u044c \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0410, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0442\u0438\u043f <code>Msg<\/code> \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 C. \u041f\u0440\u0430\u0432\u043a\u0438 \u043d\u0443\u0436\u043d\u044b \u0431\u0443\u0434\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445 \u0410 \u0438 \u0421, \u0430 \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 B (\u0438\u043b\u0438 \u0432 \u043b\u044e\u0431\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u044b\u043b\u0438 \u0431\u044b \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438) \u2014 \u0443\u0436\u0435 \u043d\u0435\u0442.<\/p>\n<\/li>\n<\/ul>\n<h2>\u041f\u043e\u0434\u0445\u043e\u0434 Layout + Modal<\/h2>\n<p>Layout + Modal \u2014 \u044d\u0442\u043e \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 (\u043c\u043e\u0434\u0430\u043b\u043a\u0438, \u043f\u043e\u043f\u0430\u043f\u044b, \u0434\u0440\u043e\u0432\u0435\u0440\u044b \u0438 \u0442. \u0434.).<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0432\u044b \u043d\u0435 \u0437\u0430\u043f\u0443\u0442\u0430\u0435\u0442\u0435\u0441\u044c: \u0441 \u044d\u0442\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442.<\/p>\n<p>\u041f\u043e\u0434\u0445\u043e\u0434 \u0431\u0430\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0445 <strong>impossible states impossible<\/strong> \u0438 <strong>onMsg<\/strong>.  \u0412\u0435\u0441\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0441\u043b\u043e\u0439 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 Layout. \u0412 \u043d\u0435\u043c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c <code>Mod\u0430lState<\/code> (\u0442. \u0435. \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u043a\u0430\u043a\u043e\u0435 \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e \u0438\u043b\u0438 \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c).<\/p>\n<pre><code class=\"typescript\">export const Layout = ({ onMsg }: Props) => {   const [search, setSearch] = useState&lt;string>('');    const gatewayList = useGatewayList();    return (     &lt;>       &lt;Toolbar.Container>         &lt;Toolbar.Input           placeholder='\u041f\u043e\u0438\u0441\u043a'           value={search}           onChange={setSearch}         \/>       &lt;\/Toolbar.Container>       &lt;CardList>         {gatewayList.map(gateway => (           &lt;GatewayListTableIdleCard              key={gateway.id}              gateway={gateway}              onMsg={onMsg} \/>         ))}       &lt;\/CardList>     &lt;\/>   ); };  \/\/ GatewayListTableIdleCard export type Msg =   | {       type: 'edit_gateway_clicked';       data: Gateway;     }   | { type: 'delete_gateway_clicked'; data: Gateway };  type Props = {   gateway: Gateway;   onMsg: (msg: Msg) => void; };  export function GatewayListTableIdleCard({ gateway, onMsg }: Props) {   const actions = useMemo(() => [     {       name: '\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c',       onClick() {         onMsg({           type: 'edit_gateway_clicked',           data: gateway,         });       },     },     {       name: '\u0423\u0434\u0430\u043b\u0438\u0442\u044c',       onClick() {         onMsg({           type: 'delete_gateway_clicked',           data: gateway,         });       },     },   ], []);    return (     &lt;GatewayListTableCard>       &lt;Header>         &lt;Title>           &lt;Name to={gateway.id}>{gateway.name}&lt;\/Name>         &lt;\/Title>         &lt;DropdownMenu actions={actions}>           &lt;ButtonIcon icon={&lt;MoreInterfaceSVG \/>} \/>         &lt;\/DropdownMenu>       &lt;\/Header>       {         \/\/ ...       }     &lt;\/GatewayListTableCard>   ); } <\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u0432\u0435\u0440\u0445 Layout, \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0432 Modal (\u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438\u0445 \u0437\u0430\u043a\u0440\u044b\u0442\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438). \u041d\u0430 \u043a\u0430\u0436\u0434\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u044b \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c \u0441\u0432\u043e\u0439 view, \u0430 \u0432 props \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0439 \u043d\u0430\u043c callback <code>onMsg<\/code>.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0436\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0442\u0438\u043f <code>ModalState<\/code>, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u043b\u0438\u0431\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e. \u0422\u0430\u043a\u0436\u0435 \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u0442\u0430\u043a\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043a\u0430\u043a \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0435 \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430, \u043b\u0438\u0431\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f (<code>'delete_gateway_modal'<\/code> \u0438 <code>'edit_gateway_modal'<\/code>), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0434\u0438\u0441\u043f\u0430\u0442\u0447\u0438\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>onMsg<\/code>.<\/p>\n<pre><code class=\"typescript\">export type ModalState =   | { type: 'delete_gateway_modal'; gateway: Gateway }   | { type: 'edit_gateway_modal'; gateway: Gateway }   | { type: 'closed' };  export const Modal = ({ state, onMsg }: Props) => {   const [t] = useTranslation();    switch (state.type) {     case 'closed':       return (         &lt;Modal           open={false}           onClose={() => {}}           onApprove={() => {}}         \/>       );      case 'delete_gateway_modal':       return (         &lt;ActionConfirmationModal           open           onClose={() => onMsg({ type: 'close' })}           onApprove={() =>             onMsg({               type: 'confirm_delete_clicked',               gateway: state.gateway,             })           }         \/>       );      case 'edit_gateway_modal':       return (         &lt;GatewayEditorModal           open           onClose={() => onMsg({ type: 'close' })}           onApprove={(input: GatewayInput) =>             onMsg({               type: 'gateway_updated',               gateway: state.gateway,               input,             })           }         \/>       );      default:       return notReachable(state);   } }; <\/code><\/pre>\n<p>\u0412 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 (\u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u044d\u0442\u043e <code>GatewayListTable<\/code>) \u043c\u044b \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c <code>ModalState<\/code> \u0438 \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>Modal<\/code>. \u00ab\u041e\u0431\u0449\u0435\u043d\u0438\u0435\u00bb \u043c\u0435\u0436\u0434\u0443 Layout \u0438 Modal \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u044d\u0442\u043e\u043c \u0436\u0435 \u0444\u0430\u0439\u043b\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0436\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u0433\u043e <code>onMsg<\/code>.<\/p>\n<pre><code class=\"typescript\">export const GatewayListTable = () => {   const [modalState, setModalState] = useState&lt;ModalState>({ type: 'closed' });   const deleteGatewayMutation = useDeleteGatewayMutation();   const updateGatewayMutation = useUpdateGatewayMutation();    return (     &lt;Wrapper>       &lt;Layout         onMsg={msg => {           switch (msg.type) {             case 'edit_gateway_clicked':               setModalState({ type: 'edit_gateway_modal', gateway: msg.data });               break;              case 'delete_gateway_clicked':               setModalState({ type: 'delete_gateway_modal', gateway: msg.data });               break;              default:               notReachable(msg);           }         }}       \/>       &lt;Modal         state={modalState}         onMsg={msg => {           switch (msg.type) {             case 'close':               setModalState({ type: 'closed' });               break;              case 'confirm_delete_clicked':               deleteGatewayMutation.mutate({ gateway: msg.gateway });               break;              case 'gateway_updated':               updateGatewayMutation.mutate({ input: msg.input, gateway: msg.gateway });               break;              default:               notReachable(msg);           }         }}       \/>     &lt;\/Wrapper>   ); }; <\/code><\/pre>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0442\u0430\u043a\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u0434\u0430 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u044b\u043c, \u043d\u043e \u043f\u043e\u0437\u0436\u0435 \u0432\u044b \u043e\u0446\u0435\u043d\u0438\u0442\u0435 \u044d\u0444\u0444\u0435\u043a\u0442: \u0435\u0441\u043b\u0438 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0437 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u043d\u0430\u0440\u0443\u0436\u0443, \u0442\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u043e\u0449\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u00ab\u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b\u00bb \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u0435\u0439\u0441\u0430. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0438 \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b.<\/p>\n<blockquote>\n<p>\u041f\u0440\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0438 Don\u2019t Repeat Yourself \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0434\u043e\u043a\u043b\u0430\u0434 \u0414\u044d\u043d\u0430 \u0410\u0431\u0440\u0430\u043c\u043e\u0432\u0430 \u2014 <a href=\"https:\/\/www.deconstructconf.com\/2019\/dan-abramov-the-wet-codebase\">The Wet Codebase<\/a>.<\/p>\n<\/blockquote>\n<h3>\u0412\u044b\u0432\u043e\u0434<\/h3>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432 <strong>Discriminated Unions<\/strong> + <strong>Exhaustive Checks<\/strong> + <strong>Impossible States Impossible<\/strong> \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0432\u044b\u0441\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f: \u0432 \u043e\u0434\u0438\u043d \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<p>\u0414\u043e\u043a\u043b\u0430\u0434 \u0418\u043b\u044c\u0438 \u0411\u0435\u043b\u044f\u0432\u0441\u043a\u043e\u0433\u043e \u0432 \u0432\u0438\u0434\u0435\u043e\u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u043d\u0430\u0448\u0435\u043c YouTube-\u043a\u0430\u043d\u0430\u043b\u0435 <a href=\"https:\/\/www.youtube.com\/@cloud.ru_tech\">Cloud.ru Tech<\/a>. \u041f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435\u0441\u044c!<\/p>\n<hr\/>\n<p><strong>\u0427\u0442\u043e \u0435\u0449\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0432 \u0431\u043b\u043e\u0433\u0435<\/strong>:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/cloud_ru\/posts\/775204\/\">\u041f\u0440\u043e \u0444\u043e\u0440\u043c\u0430\u0442 Talks \u043d\u0430 YouTube-\u043a\u0430\u043d\u0430\u043b\u0435 Cloud.ru Tech<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/cloud_ru\/news\/762734\/\">\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u043a\u0443\u0440\u0441 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438: \u043e\u0441\u0432\u0430\u0438\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u043b\u0430\u043a\u0430 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 Cloud.ru Advanced<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/cloud_ru\/news\/763820\/\">\u0417\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435\u0441\u044c \u043d\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0432 \u0441\u0444\u0435\u0440\u0435 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439 \u0434\u043b\u044f \u0444\u0438\u0437\u043b\u0438\u0446 \u0438 \u044e\u0440\u043b\u0438\u0446 \u043e\u0442 Cloud.ru<\/a><\/p>\n<\/li>\n<\/ul>\n<\/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\/778694\/\"> https:\/\/habr.com\/ru\/articles\/778694\/<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u042f \u0418\u043b\u044c\u044f \u0411\u0435\u043b\u044f\u0432\u0441\u043a\u0438\u0439 \u2014 \u0441\u0442\u0430\u0440\u0448\u0438\u0439 Frontend-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <a href=\"https:\/\/cloud.ru\/ru?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=frontendtalks_switch_notreachable_08122023\">\u0432 Cloud.ru<\/a>. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043d\u0430 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 TypeScript, \u043f\u043e\u0432\u044b\u0441\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043a\u043e\u0434\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u044b \u0438 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0445\u0435\u043b\u043f\u0435\u0440\u0430 \u2014 notReachable. \u0415\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0437\u0430\u0433\u043b\u044f\u0434\u044b\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434 \u043a\u0430\u0442!<\/p>\n<figure class=\"\"><\/figure>\n<p>\u041f\u0440\u043e \u0447\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/p>\n<p><a href=\"#aaa\">\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 switch + notReachable<\/a><br \/> <a href=\"#bbb\">\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u0441\u0442\u0438\u043b\u0435 making impossible states impossible<\/a><br \/> <a href=\"#ccc\">\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 onMsg<\/a><br \/> <a href=\"#ddd\">\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 Layout + Modal<\/a><\/p>\n<p>\u0418\u0442\u0430\u043a, \u043d\u0430\u0447\u043d\u0435\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<p><a class=\"anchor\" name=\"aaa\" id=\"aaa\"><\/a><\/p>\n<h2>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 switch + notReachable<\/h2>\n<p>notReachable \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043d\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0445\u0435\u043b\u043f\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0435\u0449\u0435 \u043c\u043e\u0449\u043d\u0435\u0435 \u0432 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0438 \u0441\u043e switch. \u0420\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043f\u043e \u043a\u0430\u043a\u043e\u043c\u0443 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0440\u0430\u0432\u043d\u0438\u043c: \u0441\u0432\u0435\u0440\u0445\u0443 <code>assertNever<\/code>(\u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043f\u043e TypeScript), \u043f\u043e\u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0435 <code>notReachable<\/code>, \u0430 \u0432\u043d\u0438\u0437\u0443 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0431\u0435\u0437 \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<pre><code class=\"typescript\">function assertNever(x: never): never {   throw new Error(`Unexpected object: ` + x); }  function notReachable(_: never): never {   throw new Error(`Should never be reached ${_}`); }  const notReachable = (_: never): never => _; <\/code><\/pre>\n<p>\u0421\u0443\u0442\u044c \u0443 \u043d\u0438\u0445 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u0430\u044f: \u043d\u0430 \u0432\u0445\u043e\u0434\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0441 \u0442\u0438\u043f\u043e\u043c <code>never<\/code>. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c <code>never<\/code>\u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0438 \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435. \u041a\u0430\u043a \u0442\u0430\u043a\u043e\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e? \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u0445.<\/p>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440 1. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u043e\u043f\u043b\u0430\u0442\u044b \u0447\u0435\u0440\u0435\u0437 Union<\/h3>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0443 \u043d\u0430\u0441 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0435\u0441\u0442\u044c \u0442\u0438\u043f <code>PaymentMethod<\/code> \u2014 \u043e\u043f\u043b\u0430\u0442\u0430 \u043f\u043e \u043a\u0430\u0440\u0442\u0435, \u043b\u0438\u0431\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0432 \u0431\u0430\u043d\u043a. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0441\u0435 \u0442\u0438\u043f\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c\u0438, \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u2014 \u043e\u0431\u0449\u0435\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>type<\/code>, \u0431\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0449\u0435\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043c\u043e\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043b\u044e\u0431\u043e\u0435, \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u2014 \u043e\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0432\u043e \u0432\u0441\u0435\u0445 \u0442\u0438\u043f\u0430\u0445 Union.<\/p>\n<p>\u042d\u0442\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0430\u043d\u0442\u043e\u0439, \u0430 \u0441\u0430\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u2014 <strong>Discriminated Unions<\/strong> (\u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0430\u043d\u0442\u043d\u043e\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435). \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043d\u0435\u043c\u0443 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 TypeScript \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u044c \u0442\u0438\u043f\u044b \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u0449\u0435\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442.<\/p>\n<pre><code class=\"typescript\">type Card = {   type: 'card';   cardNumber: string;   cardHolder: string;   expiryDate: string;   cvcNumber: number; };  type BankTransfer = {   type: 'bankTransfer';   bankName: string;   accountNumber: string; };  type PaymentMethod = Card | BankTransfer;  type Props = {   paymentMethod: PaymentMethod; };  export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>           &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>         &lt;\/S.Wrapper>       );      default:       const _exhaustiveCheck: never = paymentMethod;       return _exhaustiveCheck;   } } <\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0430\u0448\u0438\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0438\u0441\u043a\u0440\u0438\u043c\u0438\u043d\u0430\u043d\u0442\u0430 type \u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f switch. TypeScript \u00ab\u043f\u043e\u0439\u043c\u0435\u0442\u00bb, \u0447\u0442\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>cardNumber<\/code>, <code>cardHolder<\/code>, <code>expiryDate<\/code> \u0438 <code>cvcNumber<\/code> \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043a \u0442\u0438\u043f\u0443 <code>card<\/code> \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0442\u0438\u043f\u0430 <code>card<\/code>. \u0415\u0441\u043b\u0438 \u0443 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>type<\/code> \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>card<\/code>, \u043c\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c <code>accountNumber<\/code> \u0438 <code>bankName<\/code>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 default case \u0443\u043a\u0430\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>_exhaustiveCheck<\/code> (\u0438\u0441\u0447\u0435\u0440\u043f\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438) \u0441 \u0442\u0438\u043f\u043e\u043c <code>never<\/code> \u0438 \u043f\u0440\u0438\u0441\u0432\u043e\u0438\u043c \u0435\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>paymentMethod<\/code>. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0441\u0451 \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u0435\u0437 \u043e\u0448\u0438\u0431\u043e\u043a: \u043c\u044b \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u043b\u0438 \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0432 switch, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 default case \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 (never \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e paymentMethod).<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043d\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e, \u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430 <code>notReachable<\/code>. \u0412 \u0447\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u0430 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0443\u0432\u0438\u0434\u0438\u043c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<pre><code class=\"typescript\">function notReachable(_: never): never {   throw new Error(`Should never be reached ${_}`); }  export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>           &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>         &lt;\/S.Wrapper>       );      default:       return notReachable(paymentMethod);   } } <\/code><\/pre>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0442\u0438\u043f\u0430\u043c \u043e\u043f\u043b\u0430\u0442\u044b \u0435\u0449\u0435 \u043e\u0434\u0438\u043d. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0431\u044b\u0441\u0442\u0440\u044b\u0445 \u043f\u043b\u0430\u0442\u0435\u0436\u0435\u0439 (SBP). \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f <code>SBP<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 Union <code>PaymentMethod<\/code>.<\/p>\n<pre><code class=\"typescript\">type Card = {   type: 'card';   cardNumber: string;   cardHolder: string;   expiryDate: string;   cvcNumber: number; };  type BankTransfer = {   type: 'bankTransfer';   bankName: string;   accountNumber: string; };  type SBP = {   type: 'sbp';   phoneNumber: number; };  export type PaymentMethod = Card | BankTransfer | SBP; <\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u043c, \u0441\u0440\u0430\u0437\u0443 \u0443\u0432\u0438\u0434\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430 TypeScript \u0432\u043e \u0432\u0441\u0435\u0445 \u043c\u0435\u0441\u0442\u0430\u0445, \u0433\u0434\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e switch + notReachable:<\/p>\n<pre><code class=\"typescript\">ERROR in .\/src\/components\/CheckoutForm\/CheckoutForm.tsx:16:27 TS2345: Argument of type 'SBP' is not assignable to parameter of type 'never'.     14 |     15 |     default:   > 16 |       return notReachable(paymentMethod);        |                           ^^^^^^^^^^^^^     17 |   }     18 | };     19 |  ERROR in .\/src\/components\/PaymentSection\/PaymentSection.tsx:31:27 TS2345: Argument of type 'SBP' is not assignable to parameter of type 'never'.     29 |     30 |     default:   > 31 |       return notReachable(paymentMethod);        |                           ^^^^^^^^^^^^^     32 |   }     33 | }     34 | <\/code><\/pre>\n<p>\u041d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f <code>SBP<\/code> \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>notReachable<\/code> \u0441 \u0442\u0438\u043f\u043e\u043c <code>never<\/code>. \u041d\u043e \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f notReachable \u043f\u0440\u0438 \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043a\u0430\u0442\u044c \u0432\u0441\u0435 \u043c\u0435\u0441\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u2014 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 TypeScript \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u044d\u0442\u043e \u0437\u0430 \u043d\u0430\u0441.<\/p>\n<figure class=\"\"><\/figure>\n<p>\u0427\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f <code>SBP<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435:<\/p>\n<pre><code class=\"typescript\">export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'sbp':       return (         &lt;S.Wrapper>           &lt;p>Phone number: {paymentMethod.phoneNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>           &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>         &lt;\/S.Wrapper>       );      default:       return notReachable(paymentMethod);   } } <\/code><\/pre>\n<p>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u0442, \u0435\u0441\u043b\u0438 \u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a \u0432\u043d\u043e\u0432\u044c \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u0442 \u043d\u0430\u0441 \u0432\u043d\u0435\u0441\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443\u0431\u0440\u0430\u0442\u044c <code>SBP<\/code> \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u043e\u043f\u043b\u0430\u0442\u044b?<\/p>\n<pre><code class=\"typescript\">type Card = {   type: 'card';   cardNumber: string;   cardHolder: string;   expiryDate: string;   cvcNumber: number; };  type BankTransfer = {   type: 'bankTransfer';   bankName: string;   accountNumber: string; };  export type PaymentMethod = Card | BankTransfer; <\/code><\/pre>\n<p>\u0412\u0435\u0440\u043d\u043e, \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432\u043d\u043e\u0432\u044c \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u0438\u0442 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0432\u0435\u0441\u044c \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0440\u0430\u0437\u0443 \u0443\u0434\u0430\u043b\u0438\u0442\u044c:<\/p>\n<figure class=\"\"><\/figure>\n<pre><code class=\"typescript\">ERROR in .\/src\/components\/PaymentSection\/PaymentSection.tsx:12:10 TS2678: Type '\"sbp\"' is not comparable to type '\"card\" | \"bankTransfer\"'.     10 | export function PaymentSection({ paymentMethod }: Props) {     11 |   switch (paymentMethod.type) {   > 12 |     case 'sbp':        |          ^^^^^     13 |       return (     14 |         &lt;S.Wrapper>     15 |           &lt;p>Phone number: {paymentMethod.phoneNumber}&lt;\/p>  ERROR in .\/src\/components\/PaymentSection\/PaymentSection.tsx:15:43 TS2339: Property 'phoneNumber' does not exist on type 'never'.     13 |       return (     14 |         &lt;S.Wrapper>   > 15 |           &lt;p>Phone number: {paymentMethod.phoneNumber}&lt;\/p>        |                                           ^^^^^^^^^^^     16 |         &lt;\/S.Wrapper>     17 |       );     18 | <\/code><\/pre>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u0438\u0442\u0435\u0441\u044c \u0443\u0434\u043e\u0431\u043d\u043e? \u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440.<\/p>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440 2. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0432 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 IDE<\/h3>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435, \u043a\u0430\u043a \u0443\u0434\u043e\u0431\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 IDE:<\/p>\n<ul>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0443\u0441\u0442\u043e\u0439 switch,<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u043a\u0443\u0440\u0441\u043e\u0440 \u043d\u0430 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0441\u043b\u043e\u0432\u043e switch,<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u0436\u043c\u0435\u043c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e Alt + Enter.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u043e\u0442 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u0435\u0439\u0441\u044b \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0432 \u043f\u0430\u0440\u0443 \u043a\u043b\u0438\u043a\u043e\u0432. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0445\u0435\u043b\u043f\u0435\u0440 <code>notReachable<\/code> \u0432 default case.<\/p>\n<figure class=\"\"><\/figure>\n<p>\u0418\u0437-\u0437\u0430 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 switch \u043c\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043c\u043d\u0438\u0442\u044c \u043f\u0440\u043e \u043b\u043e\u0433\u0438\u043a\u0443 default case. \u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>PaymentSection<\/code> \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0435\u0437 switch \u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u044b\u0445 if, \u043d\u043e \u0443 \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0438 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438.<\/p>\n<pre><code class=\"typescript\">export function PaymentSection({ paymentMethod }: Props) {   if (paymentMethod.type === 'card') {     return (       &lt;S.Wrapper>         &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>         &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>         &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>         &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>       &lt;\/S.Wrapper>     );   }    if (paymentMethod.type === 'bankTransfer') {     return (       &lt;S.Wrapper>         &lt;p>Account Number: {paymentMethod.accountNumber}&lt;\/p>         &lt;p>Bank name: {paymentMethod.bankName}&lt;\/p>       &lt;\/S.Wrapper>     );   }    return notReachable(paymentMethod); } <\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c If, \u0442\u043e \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u0440\u043e <code>notReachable<\/code>, \u0430 \u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c switch \u2014 \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u044f\u0437\u0430\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c default case. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0445\u0435\u043b\u043f\u0435\u0440 \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u0438\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u0438 \u043b\u044e\u0431\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 Union \u0438 \u043c\u044b \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u0435\u043c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<pre><code class=\"typescript\">export function PaymentSection({ paymentMethod }: Props) {   switch (paymentMethod.type) {     case 'card':       return (         &lt;S.Wrapper>           &lt;p>Card Number: {paymentMethod.cardNumber}&lt;\/p>           &lt;p>Card Holder: {paymentMethod.cardHolder}&lt;\/p>           &lt;p>Expiry Date: {paymentMethod.expiryDate}&lt;\/p>           &lt;p>CVC Number: {paymentMethod.cvcNumber}&lt;\/p>         &lt;\/S.Wrapper>       );      case 'bankTransfer':       return (         &lt;S.Wrapper>           &lt;p>Account Number:<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\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-362140","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/362140","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=362140"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/362140\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=362140"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=362140"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=362140"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}