Skip to content

TypeScript 5 달라진 것들 #4

Description

@doong-jo

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"] });

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions