<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>프론트엔드 첫걸음</title>
    <link>https://fe-j.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 20 May 2026 02:24:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>차정</managingEditor>
    <item>
      <title>기획서로 플로우 그리기를 AI에게 맡기기</title>
      <link>https://fe-j.tistory.com/entry/%EA%B8%B0%ED%9A%8D%EC%84%9C%EB%A1%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EA%B7%B8%EB%A6%AC%EA%B8%B0%EB%A5%BC-AI%EC%97%90%EA%B2%8C-%EB%A7%A1%EA%B8%B0%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;1. manus.io/app 에 접속한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 기획서 pdf파일 올리고 분석해서 mmd파일 만들어달라구 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buGFfp/dJMcaihOyee/X8WMeyRbq8AdPnMzhmcUD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buGFfp/dJMcaihOyee/X8WMeyRbq8AdPnMzhmcUD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buGFfp/dJMcaihOyee/X8WMeyRbq8AdPnMzhmcUD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuGFfp%2FdJMcaihOyee%2FX8WMeyRbq8AdPnMzhmcUD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;540&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 다이어그램 이름 설정 뒤 만들기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1311&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JDeR2/dJMcacWbTU4/PlU0Sx5oh5KfqvNqdBBMtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JDeR2/dJMcacWbTU4/PlU0Sx5oh5KfqvNqdBBMtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JDeR2/dJMcacWbTU4/PlU0Sx5oh5KfqvNqdBBMtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJDeR2%2FdJMcacWbTU4%2FPlU0Sx5oh5KfqvNqdBBMtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1311&quot; height=&quot;740&quot; data-origin-width=&quot;1311&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. mmd 파일을 텍스트로 복사&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blNUKo/dJMcajgF6dH/vAMckEibdp2YscPg6D20G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blNUKo/dJMcajgF6dH/vAMckEibdp2YscPg6D20G0/img.png&quot; data-alt=&quot;회사 파일이라 모자이크 처리함&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blNUKo/dJMcajgF6dH/vAMckEibdp2YscPg6D20G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblNUKo%2FdJMcajgF6dH%2FvAMckEibdp2YscPg6D20G0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;680&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회사 파일이라 모자이크 처리함&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. + 버튼 클릭해서 메르메이드 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqojlv/dJMcac2VPQZ/WineKBzJdixa1AkhBapSj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqojlv/dJMcac2VPQZ/WineKBzJdixa1AkhBapSj1/img.png&quot; data-origin-width=&quot;1321&quot; data-origin-height=&quot;949&quot; data-is-animation=&quot;false&quot; width=&quot;582&quot; height=&quot;418&quot; style=&quot;width: 68.8017%; margin-right: 10px;&quot; data-widthpercent=&quot;69.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqojlv/dJMcac2VPQZ/WineKBzJdixa1AkhBapSj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqojlv%2FdJMcac2VPQZ%2FWineKBzJdixa1AkhBapSj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1321&quot; height=&quot;949&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2gQGT/dJMcaiozfcP/K1zMXmBFqLFLm1D6O0qC2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2gQGT/dJMcaiozfcP/K1zMXmBFqLFLm1D6O0qC2K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;938&quot; data-origin-width=&quot;570&quot; width=&quot;377&quot; height=&quot;620&quot; style=&quot;width: 30.0355%;&quot; data-widthpercent=&quot;30.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2gQGT/dJMcaiozfcP/K1zMXmBFqLFLm1D6O0qC2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2gQGT%2FdJMcaiozfcP%2FK1zMXmBFqLFLm1D6O0qC2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;570&quot; height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 메모장에 복사했던 메르메이드 파일 텍스트를 붙여넣고 삽입&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YbzTD/dJMcai28fnj/VngfkDenl7DYEIsdbuXmx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YbzTD/dJMcai28fnj/VngfkDenl7DYEIsdbuXmx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YbzTD/dJMcai28fnj/VngfkDenl7DYEIsdbuXmx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYbzTD%2FdJMcai28fnj%2FVngfkDenl7DYEIsdbuXmx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1654&quot; height=&quot;978&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 이렇게 됨&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;920&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HMBU6/dJMcaa49yEW/S7uUWt8JS5sKzABM9BxuE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HMBU6/dJMcaa49yEW/S7uUWt8JS5sKzABM9BxuE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HMBU6/dJMcaa49yEW/S7uUWt8JS5sKzABM9BxuE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHMBU6%2FdJMcaa49yEW%2FS7uUWt8JS5sKzABM9BxuE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1866&quot; height=&quot;920&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;920&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/373</guid>
      <comments>https://fe-j.tistory.com/entry/%EA%B8%B0%ED%9A%8D%EC%84%9C%EB%A1%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EA%B7%B8%EB%A6%AC%EA%B8%B0%EB%A5%BC-AI%EC%97%90%EA%B2%8C-%EB%A7%A1%EA%B8%B0%EA%B8%B0#entry373comment</comments>
      <pubDate>Fri, 23 Jan 2026 14:31:00 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript 빌드 에러: Cannot find type definition file for 'minimatch'</title>
      <link>https://fe-j.tistory.com/entry/TypeScript-%EB%B9%8C%EB%93%9C-%EC%97%90%EB%9F%AC-Cannot-find-type-definition-file-for-minimatch</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하다가 빌드 중 아래와 같은 에러를 만났습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Failed to compile

Cannot find type definition file for 'minimatch'.
The file is in the program because:
Entry point for implicit type library 'minimatch' TS2688
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 프로젝트를 쓰는 다른 개발자에게서는 문제가 없었기 때문에, 제 로컬 환경이나 IDE 설정 문제라는 생각이 들었습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에러는 TypeScript가 minimatch의 타입 정의(.d.ts)를 찾지 못할 때 발생합니다.&lt;br /&gt;대표적인 원인은 다음과 같습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;로컬/글로벌 TypeScript 버전 불일치&lt;/b&gt;&lt;br /&gt;IDE가 node_modules/typescript 대신 전역 설치된 TS를 참조하면서 발생.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타입 정의 누락&lt;/b&gt;&lt;br /&gt;minimatch는 자체적으로 타입을 제공하지 않고, @types/minimatch 패키지가 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;tsconfig.json 설정 문제&lt;/b&gt;&lt;br /&gt;&quot;types&quot; 옵션에 불필요하게 minimatch가 들어가 있거나, typeRoots 설정이 꼬여서 발생할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code(Cursor)에서 로컬 TypeScript 버전 사용하기Cursor(VS Code)에서는 기본적으로 전역 TS를 참조하는데, 이걸 프로젝트 안에 있는 **로컬 TypeScript(node_modules/typescript)**를 쓰도록 바꿔주면 해결됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;934&quot; data-start=&quot;795&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;845&quot; data-start=&quot;795&quot;&gt;Ctrl+Shift+P (맥은 Cmd+Shift+P) &amp;rarr; 명령 팔레트 열기&lt;/li&gt;
&lt;li data-end=&quot;898&quot; data-start=&quot;846&quot;&gt;TypeScript: Select TypeScript Version 입력 후 실행&lt;/li&gt;
&lt;li data-end=&quot;934&quot; data-start=&quot;899&quot;&gt;&lt;b&gt;Use Workspace Version&lt;/b&gt; 선택&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정을 바꾸고 나니, 빌드 에러가 바로 사라졌습니다.&lt;/p&gt;
&lt;hr data-end=&quot;976&quot; data-start=&quot;973&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1113&quot; data-start=&quot;985&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1041&quot; data-start=&quot;985&quot;&gt;같은 프로젝트인데 나만 에러가 난다면, &lt;b&gt;IDE가 전역 TS를 참조 중일 가능성이 크다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1113&quot; data-start=&quot;1042&quot;&gt;&lt;b&gt;Cursor에서 Use Workspace Version을 선택하면 로컬 TS 버전을 사용&lt;/b&gt;하게 되어 문제 해결.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;794&quot; data-start=&quot;784&quot;&gt;설정 방법&lt;/li&gt;
&lt;li data-end=&quot;666&quot; data-start=&quot;602&quot;&gt;에러의 원인은 IDE가 &lt;b&gt;전역(global)에 설치된 TypeScript&lt;/b&gt;를 사용하면서 발생한 것이었습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>로그/문제상황</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/371</guid>
      <comments>https://fe-j.tistory.com/entry/TypeScript-%EB%B9%8C%EB%93%9C-%EC%97%90%EB%9F%AC-Cannot-find-type-definition-file-for-minimatch#entry371comment</comments>
      <pubDate>Tue, 26 Aug 2025 11:03:16 +0900</pubDate>
    </item>
    <item>
      <title>Typescript 분배 규칙</title>
      <link>https://fe-j.tistory.com/entry/Typescript-%EB%B6%84%EB%B0%B0-%EA%B7%9C%EC%B9%99</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트의 &lt;b&gt;분배 규칙(distributive conditional types)&lt;/b&gt;은 조건부 타입(T extends U ? X : Y)을 정의할 때 &lt;b&gt;제네릭 매개변수에 유니언 타입이 들어오면&lt;/b&gt;, 그 유니언 타입의 각 멤버에 대해 개별적으로 조건이 적용되는 동작을 말함.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 기본 개념&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;type Example&amp;lt;T&amp;gt; = T extends number ? 'num' : 'other';
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T가 제네릭이 아닌 &lt;b&gt;고정된 타입&lt;/b&gt;이면 그대로 조건 판별:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;type A = Example&amp;lt;number&amp;gt;; // 'num'
type B = Example&amp;lt;string&amp;gt;; // 'other'
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T가 &lt;b&gt;유니언 타입&lt;/b&gt;이면, 각 멤버별로 조건을 적용한 후 &lt;b&gt;결과를 다시 유니언으로 합침&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;type C = Example&amp;lt;number | string&amp;gt;; 
// 처리 과정:
// 1. number &amp;rarr; 'num'
// 2. string &amp;rarr; 'other'
// 결과: 'num' | 'other'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 &lt;b&gt;분배(distribution)&lt;/b&gt; 동작입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 분배 규칙이 동작하는 조건&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제네릭 타입 매개변수가 &lt;b&gt;조건문의 좌변&lt;/b&gt;에 그대로 사용되어야 함&lt;/li&gt;
&lt;li&gt;T extends U ? X : Y 형태일 때만 자동 분배됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type Distributed&amp;lt;T&amp;gt; = T extends number ? 'num' : 'other';
type D1 = Distributed&amp;lt;number | string&amp;gt;; // 'num' | 'other'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 분배를 막는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T를 &lt;b&gt;한 번 래핑&lt;/b&gt;하면 분배가 일어나지 않음:&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;type NonDistributed&amp;lt;T&amp;gt; = [T] extends [number] ? 'num' : 'other';
type D2 = NonDistributed&amp;lt;number | string&amp;gt;; 
// [number | string] extends [number] &amp;rarr; false
// 결과: 'other'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 **&quot;분배 방지(disable distribution)&quot;**라고 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 활용 예시&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 Exclude 타입&lt;/h3&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;type Exclude&amp;lt;T, U&amp;gt; = T extends U ? never : T;

type E1 = Exclude&amp;lt;'a' | 'b' | 'c', 'a'&amp;gt;;
// 처리 과정:
// 'a' extends 'a' &amp;rarr; never
// 'b' extends 'a' &amp;rarr; 'b'
// 'c' extends 'a' &amp;rarr; 'c'
// 결과: 'b' | 'c'
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 Extract 타입&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;type Extract&amp;lt;T, U&amp;gt; = T extends U ? T : never;

type E2 = Extract&amp;lt;'a' | 'b' | 'c', 'a' | 'b'&amp;gt;;
// 결과: 'a' | 'b'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 핵심 정리&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;유니언 타입이 제네릭에 들어오면, 조건부 타입이 멤버별로 적용된다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조건문의 좌변&lt;/b&gt;에 T가 직접 와야 분배됨.&lt;/li&gt;
&lt;li&gt;분배를 막으려면 T를 [T]처럼 튜플 등으로 감싸면 된다.&lt;/li&gt;
&lt;li&gt;Exclude, Extract 같은 기본 유틸리티 타입도 이 규칙을 기반으로 동작한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 공부/Typescript</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/369</guid>
      <comments>https://fe-j.tistory.com/entry/Typescript-%EB%B6%84%EB%B0%B0-%EA%B7%9C%EC%B9%99#entry369comment</comments>
      <pubDate>Mon, 11 Aug 2025 16:43:55 +0900</pubDate>
    </item>
    <item>
      <title>[기본문법 배우기]  Utility Types</title>
      <link>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-Utility-Types</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Partial&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partial&amp;lt;T&amp;gt;: T의 모든 속성을 옵셔널로 변경.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;type Partial&amp;lt;T&amp;gt; = { [P in keyof T]?: T[P] };

interface User { id: number; name: string }
const u: Partial&amp;lt;User&amp;gt; = { name: &quot;Kim&quot; }; // id 없어도 OK
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Pick&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pick&amp;lt;T, K&amp;gt;: T에서 K 속성만 추출.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;type Pick&amp;lt;T, K extends keyof T&amp;gt; = { [P in K]: T[P] };

type UserName = Pick&amp;lt;User, &quot;name&quot;&amp;gt;; // { name: string }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Omit, Exclude, Extract 타입 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Omit&amp;lt;T, K&amp;gt;&lt;/b&gt;: T에서 K 속성을 제거&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Exclude&amp;lt;T, U&amp;gt;&lt;/b&gt;: T에서 U에 해당하는 타입 제거&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Extract&amp;lt;T, U&amp;gt;&lt;/b&gt;: T에서 U에 해당하는 타입만 남김&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type UserWithoutId = Omit&amp;lt;User, &quot;id&quot;&amp;gt;; // { name: string }
type NumbersOnly = Exclude&amp;lt;string | number, string&amp;gt;; // number
type OnlyStrings = Extract&amp;lt;string | number, string&amp;gt;; // string
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Required, Record, NonNullable&amp;nbsp;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Required&lt;/b&gt;: 모든 속성을 필수로 변경&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Record&amp;lt;K, T&amp;gt;&lt;/b&gt;: 키 K 집합의 모든 속성이 T 타입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NonNullable&lt;/b&gt;: null과 undefined 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;type RequiredUser = Required&amp;lt;Partial&amp;lt;User&amp;gt;&amp;gt;; // 다시 전부 필수
type StringMap = Record&amp;lt;&quot;a&quot; | &quot;b&quot;, string&amp;gt;; // { a: string; b: string }
type WithoutNull = NonNullable&amp;lt;string | null | undefined&amp;gt;; // string
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;infer&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;infer는 조건부 타입에서 타입을 추론할 때 사용.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type ReturnType&amp;lt;T&amp;gt; = T extends (...args: any[]) =&amp;gt; infer R ? R : never;

function add(a: number, b: number) { return a + b; }
type AddReturn = ReturnType&amp;lt;typeof add&amp;gt;; // number
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;완전 복잡한 타입 분석하기 (Promise와 Awaited)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Awaited&amp;lt;T&amp;gt;: Promise 내부의 값 타입 추출.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type Awaited&amp;lt;T&amp;gt; = T extends PromiseLike&amp;lt;infer U&amp;gt; ? Awaited&amp;lt;U&amp;gt; : T;

type R = Awaited&amp;lt;Promise&amp;lt;Promise&amp;lt;number&amp;gt;&amp;gt;&amp;gt;; // number
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;완전 복잡한 타입 분석하기 (bind)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bind 메서드 타입 정의는 this 타입과 매개변수, 반환 타입을 모두 연결.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;function bindExample(this: { name: string }) {
  console.log(this.name);
}
const bound = bindExample.bind({ name: &quot;Kim&quot; }); // this 고정
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;완전 복잡한 타입 분석하기 (flat )&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flat은 다차원 배열을 지정한 깊이만큼 평탄화.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;type FlatArray&amp;lt;T&amp;gt; = T extends ReadonlyArray&amp;lt;infer Inner&amp;gt;
  ? Inner extends ReadonlyArray&amp;lt;any&amp;gt; ? FlatArray&amp;lt;Inner&amp;gt; : Inner
  : T;

type R = FlatArray&amp;lt;number[][][]&amp;gt;; // number
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>강의 노트/타입스크립트 올인원 : Part1. 기본 문법편</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/368</guid>
      <comments>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-Utility-Types#entry368comment</comments>
      <pubDate>Mon, 11 Aug 2025 08:25:21 +0900</pubDate>
    </item>
    <item>
      <title>[기본 문법 배우기] lib.es5.d.ts 분석</title>
      <link>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-libes5dts-%EB%B6%84%EC%84%9D</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;forEach, map 제네릭 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript의 Array&amp;lt;T&amp;gt;는 제네릭을 활용해 각 메서드의 매개변수 타입을 배열 요소 타입과 연결합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;interface Array&amp;lt;T&amp;gt; {
  forEach(callbackfn: (value: T, index: number, array: T[]) =&amp;gt; void): void;

  map&amp;lt;U&amp;gt;(callbackfn: (value: T, index: number, array: T[]) =&amp;gt; U): U[];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T : 배열 요소 타입&lt;/li&gt;
&lt;li&gt;U : map 실행 결과 요소 타입 (콜백 반환값 타입)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;[1, 2, 3].forEach((v) =&amp;gt; console.log(v)); // v: number
const lengths = [&quot;a&quot;, &quot;bb&quot;].map(s =&amp;gt; s.length); // lengths: number[]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;filter 제네릭 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;filter는 &lt;b&gt;타입 가드&lt;/b&gt;를 활용해 결과 배열의 타입을 좁힐 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;filter(
  predicate: (value: T, index: number, array: T[]) =&amp;gt; value is S,
  thisArg?: any
): S[];
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;value is S &amp;rarr; 타입 가드: 필터링 후 S[]로 결과 타입 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const arr = [1, &quot;two&quot;, 3, &quot;four&quot;];

const numbers = arr.filter((x): x is number =&amp;gt; typeof x === &quot;number&quot;);
// numbers: number[]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;forEach 타입 직접 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장 타입 안 쓰고 직접 정의:&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type MyForEach = &amp;lt;T&amp;gt;(arr: T[], callback: (value: T, index: number, array: T[]) =&amp;gt; void) =&amp;gt; void;

const myForEach: MyForEach = (arr, callback) =&amp;gt; {
  for (let i = 0; i &amp;lt; arr.length; i++) {
    callback(arr[i], i, arr);
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;map 타입 직접 만들기&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type MyMap = &amp;lt;T, U&amp;gt;(arr: T[], callback: (value: T, index: number, array: T[]) =&amp;gt; U) =&amp;gt; U[];

const myMap: MyMap = (arr, callback) =&amp;gt; {
  const result: U[] = []; // Error &amp;rarr; 제네릭 위치상 여기선 선언 불가, 함수 안에선 타입 추론 안 됨
  return arr.map(callback);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭 &amp;lt;T, U&amp;gt;로 입력과 출력 타입을 연결.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;filter 타입 직접 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 가드 지원 버전:&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type MyFilter = {
  &amp;lt;T&amp;gt;(arr: T[], callback: (value: T, index: number, array: T[]) =&amp;gt; boolean): T[];
  &amp;lt;T, S extends T&amp;gt;(arr: T[], callback: (value: T, index: number, array: T[]) =&amp;gt; value is S): S[];
};

const myFilter: MyFilter = (arr, callback) =&amp;gt; {
  const result = [];
  for (let i = 0; i &amp;lt; arr.length; i++) {
    if (callback(arr[i], i, arr)) result.push(arr[i]);
  }
  return result;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공변성과 반공변성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;공변성&lt;/b&gt;(Covariance): 하위 타입을 상위 타입 자리에 넣는 게 허용됨 (예: 배열 요소)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반공변성&lt;/b&gt;(Contravariance): 상위 타입을 하위 타입 자리에 넣는 게 허용됨 (예: 함수 매개변수)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type Animal = { name: string };
type Dog = { name: string; bark: () =&amp;gt; void };

let animals: Animal[] = [];
let dogs: Dog[] = [];

animals = dogs; // 공변성 O
// dogs = animals; // Error
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하나에는 걸리겠지 (오버로딩)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 함수에 여러 타입 시그니처를 제공:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;function getValue(x: number): string;
function getValue(x: string): number;
function getValue(x: any): any {
  return typeof x === &quot;number&quot; ? x.toString() : x.length;
}

getValue(10);    // string
getValue(&quot;hi&quot;);  // number
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;타입스크립트는 건망증이 심하다 (+에러 처리법)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TS는 &lt;b&gt;런타임 값을 타입으로 기억하지 않음&lt;/b&gt; &amp;rarr; 변수 값은 변경 가능하므로 타입은 넓어짐.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;let v = &quot;hello&quot;; // 타입: string (리터럴 &quot;hello&quot; 아님)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const 사용&lt;/li&gt;
&lt;li&gt;as const로 리터럴 고정&lt;/li&gt;
&lt;li&gt;제네릭으로 값 타입을 캡처&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const fixed = &quot;hello&quot;; // 타입: &quot;hello&quot;
const arr = [10, 20] as const; // 타입: readonly [10, 20]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>강의 노트/타입스크립트 올인원 : Part1. 기본 문법편</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/367</guid>
      <comments>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-libes5dts-%EB%B6%84%EC%84%9D#entry367comment</comments>
      <pubDate>Mon, 11 Aug 2025 08:01:45 +0900</pubDate>
    </item>
    <item>
      <title>[기본문법 배우기] 옵셔널, 제네릭 기본</title>
      <link>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-%EC%98%B5%EC%85%94%EB%84%90-%EC%A0%9C%EB%84%A4%EB%A6%AD-%EA%B8%B0%EB%B3%B8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 기본: 호출 시점에서 타입이 정해진다&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function first&amp;lt;T&amp;gt;(arr: T[]): T {
  return arr[0];
}

const n = first([1, 2, 3]);     // T = number
const s = first([&quot;a&quot;, &quot;b&quot;]);    // T = string
const x = first&amp;lt;number&amp;gt;([10, 20]); // 명시적 지정 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출할 때 인자에 맞춰 T가 자동 추론되거나, 직접 지정할 수도 있음.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) T extends { ... } (객체 형태 제약)&lt;/h2&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;function getId&amp;lt;T extends { id: number }&amp;gt;(value: T): number {
  return value.id;
}

getId({ id: 1, name: &quot;Kim&quot; }); // OK
// getId(123); // Error
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소한 { id: number } 속성이 있는 객체만 받도록 제한.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응용 - 안전한 속성 접근:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;function pick&amp;lt;T extends object, K extends keyof T&amp;gt;(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: &quot;Lee&quot;, admin: true };
const nameVal = pick(user, &quot;name&quot;);   // string
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) T extends any[] (배열/튜플 제약)&lt;/h2&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;function head&amp;lt;T extends any[]&amp;gt;(arr: T): T[number] {
  return arr[0];
}

head([1, 2, 3]);        // number
head([&quot;x&quot;, &quot;y&quot;]);       // string
head([1, &quot;x&quot;, true]);   // number | string | boolean
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 계열만 받도록 제한, 원소 타입을 자동 추출.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플 정보 보존:&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;function tupleHead&amp;lt;T extends readonly unknown[]&amp;gt;(arr: T): T[0] {
  return arr[0];
}

const t = tupleHead([10, &quot;a&quot;] as const); // 타입: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이 제한:&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;function atLeastTwo&amp;lt;T extends [unknown, unknown, ...unknown[]]&amp;gt;(arr: T) {
  return { first: arr[0], second: arr[1] };
}

atLeastTwo([1, 2]);        // OK
atLeastTwo([1, 2, 3, 4]);  // OK
// atLeastTwo([1]); // Error
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) T extends (...args:any)=&amp;gt;any (함수 제약)&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type AnyFn = (...args: any[]) =&amp;gt; any;

function once&amp;lt;T extends AnyFn&amp;gt;(fn: T): T {
  let called = false;
  let result: ReturnType&amp;lt;T&amp;gt;;
  return function (...args: Parameters&amp;lt;T&amp;gt;) {
    if (!called) {
      called = true;
      result = fn(...args);
    }
    return result;
  } as T;
}

function add(a: number, b: number) { return a + b; }
const addOnce = once(add);

addOnce(1, 2); // OK
// addOnce(&quot;x&quot;, &quot;y&quot;); // Error
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 파라미터 타입과 반환 타입을 그대로 유지.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) T extends new (...args:any)=&amp;gt;any (생성자 제약)&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type AnyCtor = new (...args: any[]) =&amp;gt; any;

function make&amp;lt;T extends AnyCtor&amp;gt;(Ctor: T, ...args: ConstructorParameters&amp;lt;T&amp;gt;): InstanceType&amp;lt;T&amp;gt; {
  return new Ctor(...args);
}

class Person {
  constructor(public name: string, public age: number) {}
}

const p = make(Person, &quot;Han&quot;, 28); // p: Person
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConstructorParameters&amp;lt;T&amp;gt;로 생성자 매개변수 타입 추출,&lt;br /&gt;InstanceType&amp;lt;T&amp;gt;로 인스턴스 타입 추출.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-end=&quot;2454&quot; data-start=&quot;2410&quot; data-ke-size=&quot;size26&quot;&gt;6) T extends abstract new ... (추상 생성자 제약)&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1754847612163&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class Repo {
  abstract find(id: string): string;
}

class UserRepo extends Repo {
  find(id: string) { return `user-${id}`; }
}

type AbstractCtor&amp;lt;T = object&amp;gt; = abstract new (...args: any[]) =&amp;gt; T;

function useRepo&amp;lt;T extends AbstractCtor&amp;lt;Repo&amp;gt;&amp;gt;(RepoCtor: T) {
  return (instance: InstanceType&amp;lt;T&amp;gt;) =&amp;gt; instance.find(&quot;1&quot;);
}

const run = useRepo(UserRepo);
run(new UserRepo()); // OK
// run(new Repo()); // Error: Repo는 추상 클래스라 인스턴스 불가&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-end=&quot;2989&quot; data-start=&quot;2908&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2989&quot; data-start=&quot;2910&quot; data-ke-size=&quot;size16&quot;&gt;abstract new는 &amp;ldquo;이 타입은 추상 생성자&amp;rdquo;라는 뜻.&lt;br /&gt;주로 추상 클래스를 파라미터로 받는 팩토리, DI 컨테이너 등에 사용.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>강의 노트/타입스크립트 올인원 : Part1. 기본 문법편</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/366</guid>
      <comments>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-%EC%98%B5%EC%85%94%EB%84%90-%EC%A0%9C%EB%84%A4%EB%A6%AD-%EA%B8%B0%EB%B3%B8#entry366comment</comments>
      <pubDate>Mon, 11 Aug 2025 07:32:55 +0900</pubDate>
    </item>
    <item>
      <title>[기본문법 배우기] TypeScript 클래스 주요 기능 정리</title>
      <link>https://fe-j.tistory.com/entry/%EA%B8%B0%EC%B4%88%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-TypeScript%EC%9D%98-private</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. class는 실제 JavaScript에서도 남아있음&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript의 class 문법은 단순히 타입 검사 전용이 아니라 &lt;b&gt;실제 JavaScript 코드로 변환된 후에도 존재&lt;/b&gt;한다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 JavaScript로 컴파일되어도 여전히 class로 남는다.&lt;br /&gt;즉, 런타임에서도 Person은 실제 생성자 함수처럼 동작한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;반면 interface는 타입 검사 전용이므로 JS로 변환되면 사라진다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 추상 클래스 (abstract class)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abstract 키워드로 선언하는 클래스는 &lt;b&gt;직접 인스턴스를 만들 수 없고&lt;/b&gt; 반드시 상속을 통해서만 사용할 수 있다.&lt;br /&gt;또한 추상 메서드(abstract method)는 &lt;b&gt;자식 클래스에서 반드시 구현해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;abstract class Animal {
  abstract makeSound(): void; // 구현 없음, 자식 클래스에서 강제 구현

  move() {
    console.log(&quot;Moving...&quot;);
  }
}

class Dog extends Animal {
  makeSound() {
    console.log(&quot;Bark!&quot;);
  }
}

const d = new Dog();
d.makeSound(); // ✅ Bark!
d.move();      // ✅ Moving...
// const a = new Animal(); // ❌ 불가능
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공통 로직은 추상 클래스에서 구현 가능&lt;/li&gt;
&lt;li&gt;특정 메서드는 반드시 자식 클래스가 구현하도록 강제 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. public 접근 제어자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스의 속성과 메서드는 기본적으로 public이다.&lt;br /&gt;즉, public을 명시하지 않아도 어디서든 접근 가능하다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;class Car {
  public brand: string; // 어디서든 접근 가능

  constructor(brand: string) {
    this.brand = brand;
  }
}

const c = new Car(&quot;BMW&quot;);
console.log(c.brand); // ✅ 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. private&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private는 클래스 내부에서만 접근 가능한 속성을 만든다.&lt;br /&gt;즉, 클래스 외부나 상속받은 클래스에서도 접근할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;class Person {
  private name: string;

  constructor(name: string) {
    this.name = name;
  }

  introduce() {
    return `Hi, I'm ${this.name}`;
  }
}

const p = new Person(&quot;Lee&quot;);
console.log(p.introduce()); // ✅ 가능
// console.log(p.name); // ❌ 오류: private 속성은 외부 접근 불가
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;private 생성자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자에도 private를 붙일 수 있다.&lt;br /&gt;이 경우 외부에서 인스턴스를 만들 수 없으며, &lt;b&gt;싱글톤 패턴&lt;/b&gt; 구현 시 유용하다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;class Singleton {
  private static instance: Singleton;
  private constructor() {}

  static getInstance() {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
}

const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2); // ✅ true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;private vs 자바스크립트의 #&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript의 private는 &lt;b&gt;컴파일 타임에서만 제한&lt;/b&gt;된다.&lt;br /&gt;런타임에서는 단순한 속성으로 존재한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Person {
  private age = 20;
}
const p = new Person();
// JS 실행 시 p.age 접근 가능 (TS에서만 막힘)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 런타임에서도 완벽히 막고 싶다면 **ES2022의 #필드**를 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Person {
  #age = 20; // JS의 진짜 private 필드
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.&amp;nbsp; 접근 제어자 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 제어자 클래스 내부 상속 클래스 클래스 외부&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;public&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;protected&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;td&gt;❌ 불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;private&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;td&gt;❌ 불가능&lt;/td&gt;
&lt;td&gt;❌ 불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;class&lt;/b&gt; : JS에도 남아있어 실제 런타임 동작 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;abstract class&lt;/b&gt; : 직접 인스턴스 불가, 자식 클래스가 구현 강제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;public&lt;/b&gt; : 기본 접근 제어자, 어디서든 접근 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;private&lt;/b&gt; : 클래스 내부에서만 접근 가능, TS 컴파일 시에만 제한 (런타임 보장은 아님)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>강의 노트/타입스크립트 올인원 : Part1. 기본 문법편</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/364</guid>
      <comments>https://fe-j.tistory.com/entry/%EA%B8%B0%EC%B4%88%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-TypeScript%EC%9D%98-private#entry364comment</comments>
      <pubDate>Fri, 1 Aug 2025 00:12:46 +0900</pubDate>
    </item>
    <item>
      <title>[기본문법 배우기] 클래스는 &amp;quot;값&amp;quot; + &amp;quot;타입&amp;quot; 두 가지 역할을 한다</title>
      <link>https://fe-j.tistory.com/entry/7%EC%9B%94-31%EC%9D%BC-11%EC%8B%9C-31%EB%B6%84</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 클래스는 &quot;값&quot; + &quot;타입&quot; 두 가지 역할을 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서 class는 런타임에는 값(value)으로 존재하면서,&lt;br /&gt;동시에 타입 공간(type space)에서도 이름을 가진다.&lt;/p&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;class A {
  a: string;
  b: number;

  constructor(a: string, b: number = 123) {
    this.a = a;
    this.b = b;
  }

  method() {}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. type AA = A&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 A는 **클래스 A의 인스턴스 타입(instance type)**을 의미한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;type AA = A;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, AA는 new A(...) 해서 만들어지는 객체가 어떤 모양을 가지는지 나타내는 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. const a: A = new A('123')&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 A는 &lt;b&gt;인스턴스 타입&lt;/b&gt;이다.&lt;br /&gt;new A('123')의 결과(인스턴스)가 바로 타입 A에 해당한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const a: A = new A('123'); // ✅ 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, a는 A 클래스의 인스턴스이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. const b: typeof A = A&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 클래스 자체(생성자 함수 그 자체)의 타입을 말하는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A &amp;rarr; 인스턴스 타입&lt;/li&gt;
&lt;li&gt;typeof A &amp;rarr; 클래스(생성자)의 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const b: typeof A = A; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 typeof A는 &amp;ldquo;A라는 클래스 &lt;b&gt;자체&lt;/b&gt;의 타입(생성자 함수)&amp;rdquo;을 의미한다.&lt;br /&gt;즉, 다음과 같은 형태로 전개된다:&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;typeof A === { new(a: string, b?: number): A }
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;new(...) : 생성자 호출 시그니처&lt;/li&gt;
&lt;li&gt;A : 생성자로부터 만들어지는 인스턴스의 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A&lt;br /&gt;  &lt;b&gt;인스턴스 타입&lt;/b&gt; (new A()로 만든 객체의 타입)&lt;/li&gt;
&lt;li&gt;typeof A&lt;br /&gt;  &lt;b&gt;클래스 자체(생성자)의 타입&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;</description>
      <category>강의 노트/타입스크립트 올인원 : Part1. 기본 문법편</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/362</guid>
      <comments>https://fe-j.tistory.com/entry/7%EC%9B%94-31%EC%9D%BC-11%EC%8B%9C-31%EB%B6%84#entry362comment</comments>
      <pubDate>Fri, 1 Aug 2025 00:09:07 +0900</pubDate>
    </item>
    <item>
      <title>[기본문법 배우기] readonly, 인덱스드 시그니처, 맵드 타입스</title>
      <link>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-readonly-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%93%9C-%EC%8B%9C%EA%B7%B8%EB%8B%88%EC%B2%98-%EB%A7%B5%EB%93%9C-%ED%83%80%EC%9E%85%EC%8A%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. readonly&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Readonly&amp;lt;T&amp;gt;는 타입의 모든 프로퍼티를 &lt;b&gt;읽기 전용&lt;/b&gt;으로 만든다.&lt;br /&gt;즉, 값을 수정할 수 없게 한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;interface User {
  id: number;
  name: string;
}

const u: Readonly&amp;lt;User&amp;gt; = {
  id: 1,
  name: &quot;Kim&quot;
};

u.id = 2;   // ❌ 오류 발생 (읽기 전용)
u.name = &quot;Lee&quot;; // ❌ 오류 발생
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Readonly&amp;lt;T&amp;gt;는 타입 레벨에서만 적용된다.&lt;/li&gt;
&lt;li&gt;실제 JS 런타임에서는 값이 변하는 것을 막지 못한다. (Object.freeze 같은 것은 따로 사용해야 한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 인덱스드 시그니처 (Indexed Signatures)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스드 시그니처는 **동적 키(key)**를 가지는 객체 타입을 정의할 때 사용한다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;interface StringMap {
  [key: string]: string;
}

const colors: StringMap = {
  red: &quot;#ff0000&quot;,
  blue: &quot;#0000ff&quot;,
};

colors[&quot;green&quot;] = &quot;#00ff00&quot;; // ✅ 가능
colors[123] = &quot;#123456&quot;;     // ✅ number도 string으로 변환됨
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[key: string]: T : 모든 키가 string일 때 T 값을 갖는다.&lt;/li&gt;
&lt;li&gt;[key: number]: T : 모든 키가 number일 때 T 값을 갖는다.&lt;/li&gt;
&lt;li&gt;일반적으로 number 인덱스는 자동으로 string으로 변환되어 처리된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 맵드 타입 (Mapped Types)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵드 타입은 &lt;b&gt;기존 타입을 기반으로 새로운 타입을 만드는 문법&lt;/b&gt;이다.&lt;br /&gt;in 키워드를 사용하여 타입의 키들을 순회한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 예시&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;type B = 'Human' | 'Mammal' | 'Animal';

// 유니온 타입 B의 모든 요소를 key로, 값은 number로
type A = { [key in B]: number };

const aaaa: A = { 
  Human: 123, 
  Mammal: 6, 
  Animal: 7 
}; // 정상 동작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1753973851283&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface User {
  id: number;
  name: string;
  age: number;
}

// 모든 프로퍼티를 선택적으로 만든다
type PartialUser = {
  [K in keyof User]?: User[K];
};

// 모든 프로퍼티를 읽기 전용으로 만든다
type ReadonlyUser = {
  [K in keyof User]: Readonly&amp;lt;User[K]&amp;gt;;
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;활용 예시: 유틸리티 타입 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트의 기본 제공 유틸리티 타입들도 맵드 타입으로 구현된다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;// Partial&amp;lt;T&amp;gt; : 모든 프로퍼티를 선택적으로
type MyPartial&amp;lt;T&amp;gt; = {
  [K in keyof T]?: T[K];
};

// Required&amp;lt;T&amp;gt; : 모든 프로퍼티를 필수로
type MyRequired&amp;lt;T&amp;gt; = {
  [K in keyof T]-?: T[K];
};

// Readonly&amp;lt;T&amp;gt; : 모든 프로퍼티를 읽기 전용으로
type MyReadonly&amp;lt;T&amp;gt; = {
  [K in keyof T]: Readonly&amp;lt;T[K]&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Readonly&lt;/b&gt; : 객체 속성을 수정 불가능하게 만든다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱스드 시그니처&lt;/b&gt; : 동적 키를 가진 객체 타입을 정의할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;맵드 타입&lt;/b&gt; : 기존 타입을 기반으로 새로운 타입을 정의할 수 있으며, Partial, Readonly, Required 같은 유틸리티 타입들이 이 개념으로 만들어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/363</guid>
      <comments>https://fe-j.tistory.com/entry/%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0-readonly-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%93%9C-%EC%8B%9C%EA%B7%B8%EB%8B%88%EC%B2%98-%EB%A7%B5%EB%93%9C-%ED%83%80%EC%9E%85%EC%8A%A4#entry363comment</comments>
      <pubDate>Thu, 31 Jul 2025 23:57:50 +0900</pubDate>
    </item>
    <item>
      <title>[기본문법 배우기] {}와 Object (ts 4.8)</title>
      <link>https://fe-j.tistory.com/entry/%EB%B8%94%EB%A1%9C%EA%B7%B8-%EA%B8%B0%EB%A1%9D-31%EC%9D%BC-1121</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1141&quot; data-start=&quot;981&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1023&quot; data-start=&quot;981&quot;&gt;이전에는 object와 Object가 혼용되어 혼란이 있었다.&lt;/li&gt;
&lt;li data-end=&quot;1074&quot; data-start=&quot;1024&quot;&gt;4.8 이후 object는 보다 좁은 타입으로, &lt;b&gt;&quot;객체만&quot;&lt;/b&gt; 을 표현한다.&lt;/li&gt;
&lt;li data-end=&quot;1141&quot; data-start=&quot;1075&quot;&gt;반면 Object는 훨씬 넓은 타입으로, 원시값까지 포함하여 null과 undefined만 제외한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;128&quot; data-start=&quot;96&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;TypeScript 4.8에서의 object 타입&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-end=&quot;154&quot; data-start=&quot;130&quot; data-ke-size=&quot;size23&quot;&gt;1. object 타입의 의미&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;366&quot; data-start=&quot;155&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;210&quot; data-start=&quot;155&quot;&gt;object 타입은 &lt;b&gt;원시 타입(primitive)&lt;/b&gt; 을 제외한 모든 값을 의미한다.&lt;/li&gt;
&lt;li data-end=&quot;301&quot; data-start=&quot;211&quot;&gt;원시 타입에는 string, number, boolean, symbol, bigint, null, undefined 가 포함된다.&lt;/li&gt;
&lt;li data-end=&quot;366&quot; data-start=&quot;302&quot;&gt;따라서 object 타입에는 {}, 배열, 함수, new Date() 같은 값이 할당될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1753973384035&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a: object;

a = { x: 1 };   // 가능하다.
a = [1, 2, 3];  // 가능하다.
a = () =&amp;gt; {};   // 가능하다.

a = 10;         // 불가능하다.
a = &quot;hi&quot;;       // 불가능하다.
a = null;       // 불가능하다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. object와 Object의 차이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript 4.8에서는 두 타입이 더욱 명확히 구분되었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;722&quot; data-start=&quot;627&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;667&quot; data-start=&quot;627&quot;&gt;object: 원시 타입을 제외한 &lt;b&gt;객체 값만 허용&lt;/b&gt;한다.&lt;/li&gt;
&lt;li data-end=&quot;722&quot; data-start=&quot;668&quot;&gt;Object: null과 undefined를 제외한 &lt;b&gt;모든 값&lt;/b&gt;을 허용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1753973442203&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x: object;
x = {};        // 가능하다.
x = [];        // 가능하다.
x = 123;       // 불가능하다.

let y: Object;
y = {};        // 가능하다.
y = [];        // 가능하다.
y = 123;       // 가능하다.
y = &quot;hi&quot;;      // 가능하다.
y = null;      // 불가능하다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-start=&quot;493&quot; data-end=&quot;512&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-start=&quot;493&quot; data-end=&quot;512&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;TypeScript 4.8 에서의 변화&amp;nbsp; -&amp;nbsp; unknown 타입을 좁히는 방식&lt;/b&gt;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-start=&quot;493&quot; data-end=&quot;512&quot; data-ke-size=&quot;size23&quot;&gt;4.8 이전의 동작&lt;/h3&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1753973478910&quot; class=&quot;actionscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function f(x: unknown) {
  if (x) {
    // 여기서 x는 여전히 unknown이다.
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;594&quot; data-end=&quot;675&quot;&gt;
&lt;li data-start=&quot;594&quot; data-end=&quot;647&quot;&gt;if (x) 로 truthy 체크를 해도 타입은 여전히 unknown으로 남았다.&lt;/li&gt;
&lt;li data-start=&quot;648&quot; data-end=&quot;675&quot;&gt;즉, 타입스크립트는 좁히기를 하지 않았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4.8 이후의 동작&lt;/h3&gt;
&lt;pre id=&quot;code_1753973478911&quot; class=&quot;actionscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function f(x: unknown) {
  if (x) {
    // 여기서 x는 {} 타입이다.
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;778&quot; data-end=&quot;830&quot;&gt;이제 if (x)라고 하면 null과 undefined는 자동으로 제외된다.&lt;/li&gt;
&lt;li data-start=&quot;831&quot; data-end=&quot;877&quot;&gt;타입스크립트는 &quot;적어도 객체든, 원시값이든 무언가 값은 있다&quot;라고 추론한다.&lt;/li&gt;
&lt;li data-start=&quot;878&quot; data-end=&quot;920&quot;&gt;따라서 이 시점의 타입은 unknown &amp;rarr; {} 로 좁혀진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h2 data-end=&quot;102&quot; data-start=&quot;89&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;102&quot; data-start=&quot;89&quot; data-ke-size=&quot;size26&quot;&gt;{} 와 Object의 차이&lt;/h2&gt;
&lt;h2 data-end=&quot;102&quot; data-start=&quot;89&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;102&quot; data-start=&quot;89&quot; data-ke-size=&quot;size26&quot;&gt;1. {} 타입&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;258&quot; data-start=&quot;103&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;151&quot; data-start=&quot;103&quot;&gt;의미: &lt;b&gt;null과 undefined를 제외한 모든 값&lt;/b&gt;을 의미한다.&lt;/li&gt;
&lt;li data-end=&quot;258&quot; data-start=&quot;152&quot;&gt;즉, 원시값(number, string, boolean, symbol, bigint) + 객체(object, array, function) 모두 허용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1753973226808&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a: {};
a = 123;        // 가능하다.
a = &quot;hi&quot;;       // 가능하다.
a = {};         // 가능하다.
a = [];         // 가능하다.
a = () =&amp;gt; {};   // 가능하다.
a = null;       // 불가능하다.
a = undefined;  // 불가능하다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 {}는 &lt;b&gt;&quot;값이 무언가 있다&quot;&lt;/b&gt; 정도만 보장하는 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Object 타입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;568&quot; data-start=&quot;524&quot;&gt;의미: 자바스크립트의 &lt;b&gt;Object 타입 래퍼&lt;/b&gt;와 유사하게 동작한다.&lt;/li&gt;
&lt;li data-end=&quot;666&quot; data-start=&quot;569&quot;&gt;{} 와 거의 비슷하지만, 내부적으로 타입 체커가 toString, valueOf 같은 Object.prototype의 메서드를 사용할 수 있다고 본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;타입&lt;/td&gt;
&lt;td&gt;허용되는 값&lt;/td&gt;
&lt;td&gt;특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{}&lt;/td&gt;
&lt;td&gt;null과 undefined 제외한 모든 값&lt;/td&gt;
&lt;td&gt;단순히 값이 존재함만 보장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object&lt;/td&gt;
&lt;td&gt;null과 undefined 제외한 모든 값&lt;/td&gt;
&lt;td&gt;Object.prototype 메서드 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;512&quot; data-start=&quot;493&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>강의 노트/타입스크립트 올인원 : Part1. 기본 문법편</category>
      <author>차정</author>
      <guid isPermaLink="true">https://fe-j.tistory.com/361</guid>
      <comments>https://fe-j.tistory.com/entry/%EB%B8%94%EB%A1%9C%EA%B7%B8-%EA%B8%B0%EB%A1%9D-31%EC%9D%BC-1121#entry361comment</comments>
      <pubDate>Thu, 31 Jul 2025 23:21:40 +0900</pubDate>
    </item>
  </channel>
</rss>