FEStudy-boostcamp / FEStudy

0 stars 0 forks source link

TypeScript 5 달라진 것들 #4

Open doong-jo opened 1 year ago

doong-jo commented 1 year ago

TypeScript 5

Decorators

Untitled

기존의 알맹이(Object)에 껍데기(Decorator)를 씌운다.

Before

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    greet() {
        console.log(`Hello, my name is ${this.name}.`);
    }
}
const p = new Person("Ray");
p.greet();
class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    greet() {
    // 로깅 로직 추가
        console.log("LOG: Entering method.");
        console.log(`Hello, my name is ${this.name}.`);
        console.log("LOG: Exiting method.")
    }
}
class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    greet() {
    // 로깅 로직 추가
        console.log("LOG: Entering method.");
        console.log(`Hello, my name is ${this.name}.`);
        console.log("LOG: Exiting method.")
    }

    hello() {
    // 로깅 로직 추가
        console.log("LOG: Entering method.");
        console.log(`Hello, my name is ${this.name}.`);
        console.log("LOG: Exiting method.")
    }
}

Use Decorator

function loggedMethod(originalMethod: any, _context: any) {
    function replacementMethod(this: any, ...args: any[]) {
        console.log("LOG: Entering method.")
        const result = originalMethod.call(this, ...args);
        console.log("LOG: Exiting method.")
        return result;
    }
    return replacementMethod;
}
class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
        // great이 loggedMethod의 인수로 전달된다.
    @loggedMethod
    greet() {
        console.log(`Hello, my name is ${this.name}.`);
    }

    @loggedMethod
    hello() {
        console.log(`Hello, my name is ${this.name}.`);
    }
}
const p = new Person("Ray");
p.greet();
// Output:
//
//   LOG: Entering method.
//   Hello, my name is Ray.
//   LOG: Exiting method.

주의할 점

  1. class 내부 에서만 사용할 수 있다.
  2. runtime에 호출 된다.

실제 활용 사례

에러 클래스 생성 및 로깅 로직은 decorator로 재사용 가능하도록 활용

import { captureException } from 'sentry/nextjs';

function logSentry(targetClass: any) {
  // the new constructor behaviour
  const wrapper : any = function (...args) {
    // setnry에 로깅
    sentry.captureException(error, { level: 'error', extra: { args } });

    return new original(...args);
  }

  wrapper.prototype = targetClass.prototype;

  return wrapper;
}

@logSentry
export class ImageError extends Error {
  constructor(imageUrl: string, pathname: string, level?: Sentry.SeverityLevel) {
    super(`${imageUrl}을 로드하는 중 에러가 발생했습니다 (page url: ${pathname})`);

    this.name = 'ImageError';
  }
}

@logSentry
export class NetworkError extends Error {
  public statusCode: number;
  constructor(statusCode: number, responseData?: string | Record<string, any>) {
    const message =
      typeof responseData === 'string'
        ? `${statusCode}: ${responseData}`
        : `${statusCode}: ${JSON.stringify(responseData)}`;

    super(message);

    this.name = 'NetworkError';
  }
}

const Type Parameters

TS Playground

type HasNames = { names: readonly string[] };
function getNamesExactly<T extends HasNames>(arg: T): T["names"] {
    return arg.names;
}

// 우리가 원하는 타입:
//    readonly ["Alice", "Bob", "Eve"]
// 하지만 실제 추론된 타입:
//    string[]
const names1 = getNamesExactly({ names: ["Alice", "Bob", "Eve"]});
// 하지만 우리는 정확하게 아래처럼 되길 원합니다. 하지만 `as const`를 항상 붙이는 것은 잊기 쉽습니다.
// readonly ["Alice", "Bob", "Eve"]
const names2 = getNamesExactly({ names: ["Alice", "Bob", "Eve"]} as const);

//////////////// TypeScript 5, const Type Parameters 이후 //////////////////////////

type TS5_HasNames = { names: readonly string[] };
function ts5_getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
//                           ^^^^^
    return arg.names;
}
// 추론된 타입: readonly ["Alice", "Bob", "Eve"]
// TypeScript 4.
'as const' 가 불필요
const names = ts5_getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
ed-jinyoung-park commented 1 year ago

All enums Are Union enums

const a: E.Blah = 10


### ``enum`` vs ``union type``
```ts
// enum
enum Fruit {
    APPLE = 'APPLE',
    BANANA = 'BANANA',
    KIWI = 'KIWI'
}

// union type
type Fruit = 'apple' | 'banana' | 'kiwi'

ts playground

Speed, Memory, and Package Size Optimizations

image

build 속도 향상

// Accessing the exported member const value = MyNamespace.myVariable;


  - modules 방식은 top scope에 모든 것들을 선언하여 직접 호출이 가능

### package 용량 감소
- 63.5MB에서 35.6MB로 약 40% 감소
- 중복되는 파일들 제거
- bundle file의 indent 축소 (4space -> 2space)
- ``tsc.js``와  ``typingsInstaller.js``에서의 tree shaking