<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>zerozoo-front.log</title>
        <link>https://velog.io/</link>
        <description>이 블로그의 글은 제 생각을 정리한 글과 인터넷 어딘가에서 배운 것을 정리한 글입니다.  출처는 되도록 남기도록 하겠습니다. 수정 및 건의 오류 등이 있으면 언제든지 댓글 부탁드립니다.</description>
        <lastBuildDate>Tue, 22 Mar 2022 10:02:34 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>zerozoo-front.log</title>
            <url>https://images.velog.io/images/zerozoo-front/profile/66ccd4e5-041a-4fcb-a29e-ec18c17b6c9e/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. zerozoo-front.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/zerozoo-front" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[debounce 2]]></title>
            <link>https://velog.io/@zerozoo-front/debounce-2</link>
            <guid>https://velog.io/@zerozoo-front/debounce-2</guid>
            <pubDate>Tue, 22 Mar 2022 10:02:34 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-tsx">import { useCallback, useEffect, useState } from &#39;react&#39;;

const useDebounce = ({ func, delay, deps }) =&gt; {
  const callback = useCallback(func, deps);

  useEffect(() =&gt; {
    const timer = setTimeout(() =&gt; {
      callback();
    }, delay);

    return () =&gt; {
      clearTimeout(timer);
    };
  }, [callback, delay]);
};

export { useDebounce };
</code></pre>
<pre><code class="language-tsx">import { useMemo, useState, ChangeEvent, MouseEvent } from &#39;react&#39;;
import { useDebounce } from &#39;util/hooks/useDebounce&#39;;

const About = () =&gt; {
  const [second, setSecond] = useState&lt;string&gt;(&#39;&#39;);
  const [timer, setTimer] = useState&lt;any&gt;(0);
  const handleOnChangeSecond = (e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    setSecond(e.target.value as string);
    if (timer) {
      console.log(&#39;clear&#39;, timer);
      clearTimeout(timer);
    }
    const newTimer = setTimeout(() =&gt; {
      console.log(&#39;hi!&#39;, e.target.value);
    }, 800);
    setTimer(newTimer);
    return;
  };
  const handleNormalOne = () =&gt; {
    console.log(&#39;hi!&#39;);
  };

  useDebounce({
    func: () =&gt; {
      console.log(&#39;debounce????&#39;);
    },
    delay: 1000,
    deps: [second],
  });

  return (
    &lt;div role=&quot;heading&quot;&gt;
      &lt;div&gt;
        &lt;h2&gt;second&lt;/h2&gt;
        &lt;h4&gt;{second}&lt;/h4&gt;
      &lt;/div&gt;
      &lt;input onChange={handleOnChangeSecond} /&gt;
      &lt;input onChange={handleNormalOne} /&gt;
    &lt;/div&gt;
  );
};

export default About;
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[react debounce]]></title>
            <link>https://velog.io/@zerozoo-front/react-debounce</link>
            <guid>https://velog.io/@zerozoo-front/react-debounce</guid>
            <pubDate>Tue, 22 Mar 2022 09:57:13 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-tsx">const About = () =&gt; {
  const [second, setSecond] = useState&lt;string&gt;(&#39;&#39;);
  const [timer, setTimer] = useState&lt;any&gt;(0);
  const handleOnChangeSecond = (e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    setSecond(e.target.value as string);
    if (timer) {
      console.log(&#39;clear&#39;, timer);
      clearTimeout(timer);
    }
    const newTimer = setTimeout(() =&gt; {
      console.log(&#39;hi!&#39;, e.target.value);
    }, 800);
    setTimer(newTimer);
    return;
  };
  const handleNormalOne = () =&gt; {
    console.log(&#39;hi!&#39;);
  };

  return (
    &lt;div role=&quot;heading&quot;&gt;
      &lt;div&gt;
        &lt;h2&gt;second&lt;/h2&gt;
        &lt;h4&gt;{second}&lt;/h4&gt;
      &lt;/div&gt;
      &lt;input onChange={handleOnChangeSecond} /&gt;
      &lt;input onChange={handleNormalOne} /&gt;
    &lt;/div&gt;
  );
};

export default About;
</code></pre>
<p>기본적인 아이디어는 호출이 많은 함수에 대해
마지막 함수만을 실행시켜주는 것이다.</p>
<p>여러함수가 (1)=&gt; (2)=&gt; (3)=&gt; ...
이런식으로 실행 요청이 들어왔다고 가정하면</p>
<p>마지막 (3)=&gt; 함수만 실행해주는 것이다.</p>
<p>이런걸 어떻게 가능하게 하는가?</p>
<p>timer를 사용하는 것이다.</p>
<p>setTimeout의 callback 함수로써 원하는 함수를 받고
그 함수의 실행 시간을 ms으로 정해놓자.</p>
<p>아 물론 많은 요청들이 쌓일 함수들의 삭제를 담당할 clearTimeout으로 요청이 있다면 삭제하고 새 요청을 쑤셔넣어준다.</p>
<p>따라서 요청 1은 요청 2에 의해 삭제되고 2는 3에 의해 삭제된다.</p>
<p>요청 3은 요청 4가 없는 경우 800ms 이후에 발동한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[extract method type from class ]]></title>
            <link>https://velog.io/@zerozoo-front/extract-method-type-from-class</link>
            <guid>https://velog.io/@zerozoo-front/extract-method-type-from-class</guid>
            <pubDate>Fri, 11 Mar 2022 08:32:04 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">type ReturnedActions&lt;T&gt; = {
    [P in keyof T]: T[P] extends (...a: any) =&gt; infer R ? {
        type: P,
        payload: R
    } : never
}[keyof T]

type AllActions = ReturnedActions&lt;Actions&gt;

Playground link

Edit If Actions only contains functions, you can also use the distributive nature of ReturnType to get the return type of all functions in Actions:

type AllActions = ReturnType&lt;Actions[keyof Actions]&gt;;</code></pre>
<pre><code class="language-ts">type ReturnedActions&lt;T&gt; = {
  // eslint-disable-next-line no-unused-vars
  [P in keyof T]: T[P] extends (...args: any) =&gt; infer R ? {
    type:P,
    payload:R
  }:never
}[keyof T]

class Test {
  constructor(){}

  hello():void{
    console.log(&#39;hello&#39;)
  }
  num():number{
    return 1
  }
}

type TestAction = ReturnType&lt;Test[keyof Test]&gt;
type Ret = ReturnedActions&lt;Test&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/zerozoo-front/post/a3ac1698-23d4-422b-b561-abead0ebd8f7/image.png" alt=""></p>
<pre><code class="language-ts">
class Drill {
  constructor() {}

  consoleHi(): string {
    return &#39;Hi&#39;;
  }
}

type DrillAction = ReturnType&lt;Drill[keyof Drill]&gt;;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[error cause]]></title>
            <link>https://velog.io/@zerozoo-front/error-cause</link>
            <guid>https://velog.io/@zerozoo-front/error-cause</guid>
            <pubDate>Tue, 08 Mar 2022 09:05:28 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-js">function doWork() {
  try {
    doSomeWork();
  } catch (err) {
    throw new Error(&#39;Some work failed&#39;, { cause: err });
  }
  try {
    doMoreWork();
  } catch (err) {
    throw new Error(&#39;More work failed&#39;, { cause: err });
  }
}

try {
  doWork();
} catch (err) {
  switch(err.message) {
    case &#39;Some work failed&#39;:
      handleSomeWorkFailure(err.cause);
      break;
    case &#39;More work failed&#39;:
      handleMoreWorkFailure(err.cause);
      break;
  }
}</code></pre>
<p>error handle with cause</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[typescript로 java의 throws를 구현하기]]></title>
            <link>https://velog.io/@zerozoo-front/typescript%EB%A1%9C-java%EC%9D%98-throws%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@zerozoo-front/typescript%EB%A1%9C-java%EC%9D%98-throws%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 07 Mar 2022 15:42:32 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">{
  class PropertyRequiredError extends Error {
    property: string;
    constructor(property: string) {
      super(&quot;No property&quot; + property);
      this.name = this.constructor.name;
      this.property = property;
    }
  }
  class ValidationError extends Error {
    constructor(message: string) {
      super(message);
      this.name = this.constructor.name;
    }
  }
  function readUser(json: string) {
    let user = JSON.parse(json);
    if (!user.age) {
      throw new PropertyRequiredError(&quot;age&quot;);
    }
    if (!user.name) {
      throw new PropertyRequiredError(&quot;name&quot;);
    }
    return true;
  }

  // 선언된 모든 error 처리를 해야만 compile이 통과된다.
  function parseSomething(): boolean | ValidationError | PropertyRequiredError {
    const data: string = &#39;{&quot;age&quot;:25}&#39;;
    try {
      return readUser(data);
    } catch (e) {
      if (e instanceof ValidationError) {
        console.error(&quot;name: &quot; + e.name);
        console.error(&quot;message: &quot; + e.message);
        console.error(&quot;stack: &quot; + e.stack);
      }
      if (e instanceof PropertyRequiredError) {
        console.error(&quot;name: &quot; + e.name);
        console.error(&quot;message: &quot; + e.message);
        console.error(&quot;stack: &quot; + e.stack);
      } else {
        throw e;
      }
      return false;
    }
  }

  parseSomething();
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[error handle에 대해]]></title>
            <link>https://velog.io/@zerozoo-front/error-handle%EC%97%90-%EB%8C%80%ED%95%B4</link>
            <guid>https://velog.io/@zerozoo-front/error-handle%EC%97%90-%EB%8C%80%ED%95%B4</guid>
            <pubDate>Mon, 07 Mar 2022 12:51:46 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">
// 처리하지 않은 에러는 다시 던지자

{
  try {
    console.log(&quot;hi&quot;);
    throw new Error(&quot;error message is hi&quot;);
  } catch (e) {
    if (e instanceof RangeError) {
      console.error(e.message);
    } else {
      throw e;
    }
  }
}</code></pre>
<pre><code class="language-ts">
// custom error를 만들자.
{
  class ValidationError extends Error {
    constructor(message: string) {
      super(message);
      this.name = &quot;ValidationError&quot;;
    }
  }

  class PropertyRequiredError extends ValidationError {
    property: string;
    constructor(property: string) {
      super(&quot;No property: &quot; + property);
      this.name = &quot;PropertyRequiredError&quot;;
      this.property = property;
    }
  }

  function readUser(json: any) {
    let user = JSON.parse(json);
    if (!user.age) {
      throw new PropertyRequiredError(&quot;age&quot;);
    }
    if (!user.name) {
      throw new PropertyRequiredError(&quot;name&quot;);
    }
    return user;
  }
  try {
    let user = readUser(&#39;{&quot;age&quot;:25}&#39;);
  } catch (e) {
    if (e instanceof ValidationError) {
      console.error(&quot;Invalid data: &quot; + e.message);
      console.error(e.name);
    } else if (e instanceof SyntaxError) {
      console.error(&quot;JSON Syntax Error: &quot; + e.message);
    } else {
      throw e;
    }
  }
}
</code></pre>
<pre><code class="language-ts">// custom한 에러


{
  class MyError extends Error {
    constructor(message: string) {
      super(message);
      this.name = this.constructor.name;
    }
  }
  class ValidationError extends MyError {}
  class PropertyRequiredError extends ValidationError {
    property: string;
    constructor(property: string) {
      super(&quot;No property: &quot; + property);
      this.property = property;
    }
  }

  console.log(&quot;name???????????&quot;, new PropertyRequiredError(&quot;field&quot;).name);
}

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[infer]]></title>
            <link>https://velog.io/@zerozoo-front/infer</link>
            <guid>https://velog.io/@zerozoo-front/infer</guid>
            <pubDate>Mon, 21 Feb 2022 15:09:40 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">export namespace Infer {
    export type ElementType&lt;T&gt; = T extends unknown[] ? T[number] : T;
    export type InferElementType&lt;T&gt; = T extends (infer U)[] ? U : T;
    export type SecondArg&lt;F&gt; = F extends (a:any,b:infer B) =&gt; any ? B : never;
}

type ElementTest = Infer.ElementType&lt;number[]&gt;; // number;
type InferElementTest = Infer.InferElementType&lt;string[]&gt; // string;
type InferElementTest1 = Infer.InferElementType&lt;string&gt; // string;

type F = typeof Array[&#39;prototype&#39;][&#39;slice&#39;]; // type F = (start?: number | undefined, end?: number | undefined) =&gt; any[] 
type SecondArg = Infer.SecondArg&lt;F&gt;;  // type SecondArg = number | undefined</code></pre>
<p>infer는 generic으로 받은 type을 tsc가 추론하여 스스로 결정하게 된다. </p>
<p>type SecondArg는 전달받은 F의 타입을 추론해 해당 인자의 타입을 끌어내어 반환해주고 있다. 이를 infer가 아닌 방법으로 어떻게 하면 끌어낼 수 있을지 두렵다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[namespace]]></title>
            <link>https://velog.io/@zerozoo-front/namespace</link>
            <guid>https://velog.io/@zerozoo-front/namespace</guid>
            <pubDate>Mon, 21 Feb 2022 14:54:46 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">export namespace Condition {
type IsString&lt;T&gt; = T extends string ? string : number;
export type Num = IsString&lt;number&gt;;
export type A = IsString&lt;string&gt;;

}
const a:Condition.A =&#39;d&#39;
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[user-defined type guard (feat: is)]]></title>
            <link>https://velog.io/@zerozoo-front/user-defined-type-guard-feat-is</link>
            <guid>https://velog.io/@zerozoo-front/user-defined-type-guard-feat-is</guid>
            <pubDate>Mon, 21 Feb 2022 14:27:31 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">function isString(a:unknown):boolean{
    return typeof a === &#39;string&#39;;
}
function parseInput(input:string|number){
    let string:string;
    if(isString(input)){
        // string = input.toUpperCase(); error!
    }
}</code></pre>
<p>위 코드는 에러가 발생한다. isString이 boolean을 준다는 것을 parseInput 함수는 알 지 못한다.</p>
<p>isString 함수 내에서만 작동하는 것이다. 따라서 isString 함수는 boolean을 반환하는 것을 알려주지만 typeguard로써의 역할을 완벽하게 해내지 못했다!</p>
<hr>
<pre><code class="language-ts">function isString(a:unknown):a is string{
    return typeof a === &#39;string&#39;;
}
function parseInput(input:string|number){
    let string:string;
    if(isString(input)){
        string = input.toUpperCase(); 
    }
}
</code></pre>
<p>is는 매개변수 타입을 정제하고 boolean을 반환하는 함수에 사용자 정의 타입 안전장치를 이용 함수가 해당 타입을 반환한다고 보장 하도록 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[key in, in, keyof mapping으로 타입스크립트 타입 선언]]></title>
            <link>https://velog.io/@zerozoo-front/key-in-in-keyof-mapping%EC%9C%BC%EB%A1%9C-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8</link>
            <guid>https://velog.io/@zerozoo-front/key-in-in-keyof-mapping%EC%9C%BC%EB%A1%9C-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8</guid>
            <pubDate>Mon, 21 Feb 2022 14:15:36 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">type WeekDay = &#39;Mon&#39;|&#39;Tue&#39;|&#39;Wed&#39;|&#39;Thu&#39;|&#39;Fri&#39;;
type Day = WeekDay|&#39;Sat&#39; |&#39;Sun&#39;;
// type Record&lt;K extends keyof any,T&gt;={
//     [P in K]:T
// }
const day:Record&lt;WeekDay,Day&gt; = {
    Fri:&#39;Fri&#39;,
    Mon:&#39;Mon&#39;,
    Thu:&#39;Thu&#39;,
    Wed:&#39;Wed&#39;,
    Tue:&#39;Tue&#39;
}

type Account = {
    id:number;
    isEmploy:boolean;
    notes:string[];
}

type ReadonlyAccount = {
    readonly [K in keyof Account]:Account[K];
}
type ReadonlyAccount2 = {
    -readonly [K in keyof Account]:Account[K];
}

type OptionalAccount = {
    [K in keyof Account]:Account[K]
}


type ABC = &#39;A&#39;|&#39;B&#39;|&#39;C&#39;;
type In = {[K in ABC]:K}

const abc0:In = {
    A:&quot;A&quot;,
    B:&quot;B&quot;,
    C:&quot;C&quot;
}
interface AB {
    A:&#39;A&#39;,
    B:&#39;B&#39;
}
type InKeyOf = {
    [K in keyof AB]:K
}
type InKeyOfKeyIn = {
    [K in keyof AB]:AB[keyof AB]
}
const abc1:InKeyOf = {
    A:&quot;A&quot;,
    B:&quot;B&quot;
}
const abc2:InKeyOfKeyIn = {
    A:&quot;B&quot;,
    B:&quot;A&quot;
}


const d:OptionalAccount = {
    id:1,
    isEmploy:true,
    notes:[&#39;w&#39;]
}
d.id=2; // ok
const dd:ReadonlyAccount = {
    id:1,
    isEmploy:true,
    notes:[&#39;w&#39;]
}
// dd.id = 2; // error!

const ddd:ReadonlyAccount2 = {
    id:1,
    isEmploy:true,
    notes:[&#39;w&#39;]
}
ddd.id = 2;  // ok


</code></pre>
<pre><code class="language-ts">
type Dig&lt;T, U extends keyof T&gt; = Pick&lt;T, U&gt;[U]
const obj = {
    a: {
      first: &#39;1&#39;,
      second: &#39;2&#39;,
      thrid: &#39;3&#39;,
    },
    b: &#39;b&#39;,
    c: {
      fourth: &#39;4&#39;,
      fifth: &#39;5&#39;
    }
  }

const Dig:Dig&lt;typeof obj,&#39;a&#39;&gt; = {
    first:&#39;3&#39;,
    second:&#39;4&#39;,
    thrid:&#39;5&#39;
}
type Diff&lt;T, U&gt; = T extends U ? never : T
type DiffExample = Diff&lt;&#39;a&#39; | &#39;b&#39; | &#39;c&#39; | &#39;d&#39;, &#39;a&#39; | &#39;c&#39; | &#39;f&#39;&gt;;  // &#39;b&#39; | &#39;d&#39; </code></pre>
<p>아래의 Dig, Diff는 안도형님의 글을 참고하였습니다
<a href="https://rinae.dev/posts/helper-types-in-typescript">안도형님의 블로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[typescript builder pattern]]></title>
            <link>https://velog.io/@zerozoo-front/typescript-builder-pattern</link>
            <guid>https://velog.io/@zerozoo-front/typescript-builder-pattern</guid>
            <pubDate>Sun, 20 Feb 2022 10:06:30 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">class RequestBuilder {
    private url:string | null = null;
    private method:&#39;get&#39;|&#39;post&#39;|null = null;
    private data:object | null = null;

    setURL(url:string):this{
        this.url=url;
        return this;
    }

    setMethod(method:&#39;get&#39;|&#39;post&#39;):this{
        this.method = method;
        return this;
    }

    setData(data:object):this{
        this.data = data
        return this
    }

    send(){
        if(this.url &amp;&amp; this.data &amp;&amp; this.method){
            console.log(this)
            return;
        }
        console.log(&#39;nothing!&#39;)

    }
}

const test0 = new RequestBuilder()
.setURL(&#39;url&#39;)
.setData({data:&#39;data&#39;})
.setMethod(&#39;get&#39;)
.send()

const test1 = new RequestBuilder().send();</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[typescript factory pattern]]></title>
            <link>https://velog.io/@zerozoo-front/typescript-factory-pattern</link>
            <guid>https://velog.io/@zerozoo-front/typescript-factory-pattern</guid>
            <pubDate>Sun, 20 Feb 2022 09:28:52 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">type Shoe = {
    purpose:string;
};

class BalletFlat implements Shoe {
    public purpose = &#39;dancing&#39;;
};

class Boot implements Shoe {
    purpose = &#39;woodCutting&#39;;
};

class Sneaker implements Shoe {
    purpose = &#39;walking&#39;;
};

let Shoe = {
    create(type:&#39;balletFlat&#39;|&#39;boot&#39;|&#39;sneaker&#39;):Shoe{
        switch (type){
            case &#39;balletFlat&#39;: return new BalletFlat;
            case &#39;boot&#39;:return new Boot;
            case &#39;sneaker&#39;:return new Sneaker;
        }
    }
}
const balletShoe = Shoe.create(&#39;balletFlat&#39;);
console.log(balletShoe.purpose);</code></pre>
<p>호출자는 팩토리가 특정 인터페이스를 만족하는 클래스를 제공할 것이라는 사실만을 알아야 하고, 어떤 구체 클래스가 이 일을 하는지 알지 못해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[typescript final 따라하기]]></title>
            <link>https://velog.io/@zerozoo-front/typescript-final-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@zerozoo-front/typescript-final-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 20 Feb 2022 09:12:13 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">class Message{
    private constructor(private messages:string[]){}

    static create(messages:string[]){
        return new Message(messages);
    }
}

const msg:Message = new Message; // error!
const message:Message = Message.create([&#39;Hola!&#39;]); // good</code></pre>
<p>타입스크립트에서 final을 따라해보자.</p>
<p>final을 응용하여 싱글톤을 만들어 낼 수 있다.</p>
<p>아무튼 final이 뭔지 설명하자면 클래스나 메서드를 확장 혹은 오버라이드 할 수 없게 만드는 키워드이다. (다른 oop lang에서)</p>
<p>class의 constructor를 private으로 생성하면 해당 class를 생성, 확장할 수 없게 된다. 하지만 static method로 해당 클래스를 생성하여 돌려준다면 확장이 막힌 클래스를 생성할 수 있다.</p>
<p>final이 없으므로 이렇게 구현하면 되는 것이다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[typescript mixin 믹스인 예제]]></title>
            <link>https://velog.io/@zerozoo-front/typescript-mixin-%EB%AF%B9%EC%8A%A4%EC%9D%B8-%EC%98%88%EC%A0%9C</link>
            <guid>https://velog.io/@zerozoo-front/typescript-mixin-%EB%AF%B9%EC%8A%A4%EC%9D%B8-%EC%98%88%EC%A0%9C</guid>
            <pubDate>Sun, 20 Feb 2022 08:53:33 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">type ClassConstructor&lt;T&gt; = new (...args:any[])=&gt;T; //(1)

function withEZDebug&lt;C extends ClassConstructor&lt;{getDebugValue():object}&gt;&gt;(Class:C){
    return class extends Class {
        debug(){
            let name = this.constructor.name;
            let value =this.getDebugValue();
            return `${name}(${JSON.stringify(value)})`
        }
    }
}

class HardToDebugUser{
    constructor(
        private id:number,
        private firstName:string,
        private lastName:string
    ){}
    getDebugValue(){
        return {
            id:this.id,
            name:this.firstName + &#39; &#39; + this.lastName
        }
    }
}

let User = withEZDebug(HardToDebugUser);
let user = new User(3,&#39;Emmmmm&#39;,&#39;Gi&#39;);
console.log(user.debug()); // {id:..., name:...}</code></pre>
<p>위 코드를 간단히 요약해보자.
(1): 모든 생성자를 표현하는 ClassConstructor 타입이다. 타입스크립트는 전적으로 구조를 기반으로 타입을 판단하므로 new로 생성할 수 있는 모든 것을 생성자로 규정한다. 인자는 어떤 타입의 매개변수가 올지 알 수 없으므로 임의의 개수의 any타입을 인수로 받는다.</p>
<p>withEZDebug는 class를 받아 debug라는 메소드를 추가한 확장된 클래스를 돌려준다. 이렇게 withEZDebug는 자신이 반환하는 class와 입력 받은 class를 mix 시켜 안에 주입한다 하여 mixin 기법이라 할 수 있고 </p>
<p>보통 믹스인이라고 한다. 동작을 캡슐화하여 외부에서는 debug 함수의 정체를 모르게 할 수 있게된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[java util.Scanner]]></title>
            <link>https://velog.io/@zerozoo-front/java-util.Scanner</link>
            <guid>https://velog.io/@zerozoo-front/java-util.Scanner</guid>
            <pubDate>Tue, 15 Feb 2022 12:06:51 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">package com.company;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int i = sc.nextInt();
        System.out.println(i*5);
        sc.close();
    }
}
</code></pre>
<p>자바는 사용자의 입력을 받기 위해 nextInt() 함수를 실행시키고 그대로 코드의 실행을 묶어둔다.</p>
<hr>
<p>```java
package com.company;
import java.util.Scanner;</p>
<p>public class Main {
    public static void main(String[] args) {
        getTyping();
    }</p>
<pre><code>public static void getTyping(){
    Scanner sc = new Scanner(System.in);
    while(sc.hasNextInt()){
        System.out.println(&quot;You input &quot;+sc.nextInt());
    }
    sc.close();
}</code></pre><p>}</p>
<p>// 이런식으로 boolean method를 사용하여 입력을 누적할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Record<K,V>]]></title>
            <link>https://velog.io/@zerozoo-front/RecordKV</link>
            <guid>https://velog.io/@zerozoo-front/RecordKV</guid>
            <pubDate>Mon, 31 Jan 2022 12:17:06 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">interface Score {
  &#39;1&#39;: &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;D&#39;;
  &#39;2&#39;: &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;D&#39;;
  &#39;3&#39;: &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;D&#39;;
  &#39;4&#39;: &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;D&#39;;
}
const score: Score = {
  1: &#39;A&#39;,
  2: &#39;B&#39;,
  3: &#39;C&#39;,
  4: &#39;D&#39;,
};

const scoreWithRecord: Record&lt;&#39;1&#39; | &#39;2&#39; | &#39;3&#39; | &#39;4&#39;, &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;D&#39;&gt; = {
  1: &#39;A&#39;,
  2: &#39;B&#39;,
  3: &#39;C&#39;,
  4: &#39;D&#39;,
};

type Scores = &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;D&#39;;
type Grades = &#39;1&#39; | &#39;2&#39; | &#39;3&#39; | &#39;4&#39;;
const scoreWithTypeRecord: Record&lt;Grades, Scores&gt; = {
  1: &#39;A&#39;,
  2: &#39;B&#39;,
  3: &#39;C&#39;,
  4: &#39;D&#39;,
};</code></pre>
<pre><code class="language-ts">// validate 할 때 Record + keyof
interface User {
  id: number;
  name: string;
  age: number;
}
function isValid(user: User): Record&lt;keyof User, boolean&gt; {
  const result: Record&lt;keyof User, boolean&gt; = {
    id: user.id &gt; 0,
    name: user.name !== &#39;&#39;,
    age: user.age &gt; 0,
  };
  return result;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[class generic extends ]]></title>
            <link>https://velog.io/@zerozoo-front/class-generic-extends</link>
            <guid>https://velog.io/@zerozoo-front/class-generic-extends</guid>
            <pubDate>Mon, 31 Jan 2022 12:08:14 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">interface User {
  name: string;
  age: number;
}

interface Car {
  name: string;
  color: string;
}
interface Book {
  price: number;
}

function showName&lt;T extends { name: string }&gt;(data: T): string {
  return data.name;
}
//showName의 인자 data T는 항상 name:string prop을 가져야 한다는 제약을 걸어줌</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect & intersectionObserver API를 활용해보자.]]></title>
            <link>https://velog.io/@zerozoo-front/useEffect-intersectionObserver-API%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@zerozoo-front/useEffect-intersectionObserver-API%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Wed, 05 Jan 2022 02:09:09 GMT</pubDate>
            <description><![CDATA[<p>사용하기 나름이겠지만 navBar를 기준으로 io를 구현해보자.</p>
<p>요청받은 사항은 아래의 두가지 였다.</p>
<ol>
<li><p>화면의 최상단에 있는 경우 navBar의 배경색을 투명하게한다.</p>
</li>
<li><p>스크롤이 내려가면 화면을 따라 navBar가 따라오고 배경색이 흰색이 된다.</p>
</li>
</ol>
<pre><code>
&lt;nav&gt;
...
&lt;/nav&gt;
</code></pre><p>위와 같이 nav 태그 내부에 당신의 navBar가 있다고 하자.</p>
<p>lint에 오류가 있지만 상관하지 않고 기능을 구현하기로한다</p>
<p>우선 dom의 형태를 변경해준다.</p>
<pre><code class="language-js">&lt;div&gt;
  &lt;div /&gt; // 1
  &lt;nav&gt; // 2
  ...
  &lt;/nav&gt;

&lt;/div&gt;</code></pre>
<p>위와 같은 형태를 한 이유는 nav 태그 위의 div가 필요해서이다.</p>
<p>1의 역할은 화면에 보이면 false를 반환한다라는 이벤트를 하는 io를 심어놓는다.</p>
<p>2의 역할은 화면에 안보이면 true를 반환한다라는 이벤트를 하는 io를 심어놓는다.</p>
<p>즉 스위치를 만든 것이다.</p>
<p>아래는 실제 코드의 일부이다.</p>
<pre><code class="language-jsx">const Navigation = () =&gt; {
  const [showMenu, setShowMenu] = useState(false);
  const toggleMenu = () =&gt; setShowMenu(!showMenu);
  const [isNavBgVisible, setIsNavBgVisible] = useState(false);
  const navIoRef = useRef&lt;any&gt;();
  const ceilingIoRef = useRef&lt;any&gt;();
  const topObCallBack = () =&gt; {
    setIsNavBgVisible(false);
  };
  const navObCallBack = () =&gt; {
    console.log(`callback!`);
    setIsNavBgVisible(true);
  };
  useEffect(() =&gt; {
    const observeWhenNavDisappear: IntersectionObserver = new IntersectionObserver(([entries]: any): void =&gt; {
      if (!entries.isIntersecting) {
        navObCallBack();
        observeWhenNavDisappear.unobserve(entries.target);
      }
    });
  // lint error
    navIoRef.current &amp;&amp; observeWhenNavDisappear.observe(navIoRef.current);
  }, [navIoRef, isNavBgVisible]);
  useEffect(() =&gt; {
    const observeWhenCeilingAppear: IntersectionObserver = new IntersectionObserver(([entries]) =&gt; {
      if (entries.isIntersecting) {
        topObCallBack();
      }
    });
    // lint error
    ceilingIoRef.current &amp;&amp; observeWhenCeilingAppear.observe(ceilingIoRef.current);
  }, [ceilingIoRef, isNavBgVisible]);

  return (
    &lt;div&gt;
      &lt;div ref={ceilingIoRef} /&gt;
      &lt;nav
        ref={navIoRef}
        className={tw(`bg-white ${isNavBgVisible ? `bg-opacity-100 fixed` : `bg-opacity-0  `}  absolute z-20 w-full`)}
      &gt;
        ...
      &lt;/nav&gt;
      ...
      &lt;/div&gt;</code></pre>
<p>react의 life cycle을 고려하여 useEffect와 useRef를 사용해야 한다.
문법적으로 걸리는 부분은 주석으로 <code>lint error</code>이라 써놓았고</p>
<p>ref를 선언하는데 any 타입을 사용한 것이다.</p>
<p>하지만 위 코드의 일부는 landingpage에서 사용되고 nextjs를 거쳐 compile되기 때문에 결국 static한 html 파일이 된다.</p>
<p>따라서 lint나 type 선언이 지저분해도 어느정도는 감안이 되는 부분이다.</p>
<p>잘 생각해보면 useEffect와 io 하나에 모든 기능을 넣을 수 있겠지만 
굳이 그래야 하는가? 라는 생각도 들어 useEffect를 두개로 나누었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript algorithms / algorithms / isPositive]]></title>
            <link>https://velog.io/@zerozoo-front/javascript-algorithms-algorithms-isPositive</link>
            <guid>https://velog.io/@zerozoo-front/javascript-algorithms-algorithms-isPositive</guid>
            <pubDate>Mon, 03 Jan 2022 17:26:03 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">/**
 *
 * 8 bit로 설명해보자.
 *
 * 32 bit를 기준으로 하는 정수이므로
 * 0000 0000 0000 0000
 * 0000 0000 0000 0001 = 1
 *
 * &gt;&gt; 는 맨 왼쪽 비트의 복사본을 만들면서 이동합니다.
 * 1100 &gt;&gt; 1 = 1110
 *
 * 따라서
 *
 * 11111111 11111111
 * 11111111 11111011 &gt;&gt; 31
 * =
 * 11111111 11111111
 * 11111111 11111111 === 1 : false
 *
 *
 * 음수이면 가장 왼쪽의 비트가 1이므로
 * bit shift를 31번 하였을 때 그 값이 1이 아니면
 * bit shift를 하면서 복제된 값이 전부 1이라는 것이므로
 * 해당 param은 음수이다.
 * @param {number} number -32-bit integer.
 * @return {boolean}
 */

export default function isPositive(number: number): boolean {
  if (number === 0) {
    return false;
  }
  return ((number &gt;&gt; 31) &amp; 1) === 0;
}

console.log(isPositive(-5));
console.log(isPositive(5));
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript algorithms / algorithms / isEven]]></title>
            <link>https://velog.io/@zerozoo-front/javascript-algorithms-algorithms-isEven</link>
            <guid>https://velog.io/@zerozoo-front/javascript-algorithms-algorithms-isEven</guid>
            <pubDate>Mon, 03 Jan 2022 16:54:00 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-ts">/**
 * @param {number} number
 * @return {boolean}
 */

/**
 * 0101 5
 * 0001
 * ----
 * 0001 === 0 (false) odd
 *
 * 0110 6
 * 0001
 * -----
 * 0000 === 0 (true): even
 *
 * 0111 7
 * 0001
 * ----
 * 0001 === 0 (false): odd
 * 즉 2^0 자리의 bit가 1인 경우 n_0+n_1+n_2...4+2+1에서 1이 true이 경우
 * 앞에서 나온 모든 짝수의 합에 1이 더해져 홀수가 된다
 *
 */
export default function isEven(number: number): boolean {
  return (number &amp; 1) === 0;
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>