6/28 TIL

Hwi·2024년 6월 28일

TIL

목록 보기
59/96

📚 진행한 공부 📚

  • TypeScript 다형성, 제네릭
  • TypeScript Classes

TypeScript 다형성, 제네릭

  • poly란? => many, serveral, much, multi
  • morphos란? => form, structure
  • polymorphos란? => poly + morphos => 여러 다른 구조

즉, 여러 타입을 받아들임으로써 여러 형태를 가지는 것을 의미

📖 다형성 적용 전

interface SuperPrint {
  (arr: number[]): void;
  (arr: boolean[]): void;
  (arr: string[]): void;
  (arr: number[] | boolean[]): void;
}

const superPrint: SuperPrint = (arr) => {
  arr.forEach((elem) => console.log(elem));
};

superPrint([1, 2, 3]);
superPrint([true, false, false]);
superPrint(["a", "b"]);
superPrint([1, 2, true, false]);

이 코드는 배열을 출력해주는 메서드를 구성했는데 문제가 있음.

  • 매번 새로운 타입을 받고 싶을 때마다 새로운 타입의 배열을 받을 수 있다고 명시하기엔 비효율적

  • 아무 타입이나 들어올 수 있게 하는 것도 비효율적

그렇다면 답은 제네릭을 이용해보는 것이다.

📖 다형성 적용 후

interface SuperPrint<T> {
	(arr: T[]): void
}

const superPrint: SuperPrint<number> = (arr) => {
	arr.forEach((el) => console.log(el));
}

superPring([1,2,3]);

함수 타입을 정의할 때, 어떤 배열을 처리할 것인지만 뒤에 붙여주면 된다.

📖 타입 추론 제네릭 이용

interface SuperPrint {
	<T>(arr: T[]): void
}

const superPrint: SuperPrint = (arr) => {
	arr.forEach((el) => console.log(el));
};

superPrint([1,2,3,"4"]);

이와 같이 작성하면 제네릭의 타입을 추론해서 타입스크립트가 적용한다.

그렇지만 이 방법은 타입 추론에 기대는 방식이기에 선호하지 않고, 사실상 아무 배열이나 튜플도 들어갈 수 있기 때문에 제약이 너무 적어짐

즉, any와의 차이가 무엇일지 잘 생각해봐야 함

제네릭 = 요구한 대로 signature 를 생성해줄 수 있는 도구

📖 제네릭 상속(재사용) 사용하기

라이브러리를 만들거나, 다른 개발자가 사용할 기능을 개발하는 경우엔 제네릭이 유용할 수 있음
그 외의 경우에는 제네릭을 직접 작성할 일은 없고
사용만 하게 될 것임

interface Person<E> {
	name: string;
  extraInfo: E;
}

interface PersonDetail extends Person<string> {}

const personDetail: PersonDetail = {
	name: "HWI",
  	extraInfo: "male, developer",
};
type Man<E> = {
  name: string;
  extraInfo: E;
};

type ManDetail = Man<string>;

const manDetail: ManDetail = {
  name: "HWI",
  extraInfo: "developer, South Korea.",
};

위 코드와 같이 유연하게 상속(extends) 시에 사용할 수도 있다.

TypeScript Classes

출처 velog

  • object를 만드는 blueprint (청사진, 설계도)
  • JavaScript 에서도 ES6부터 class 키워드 사용 가능
  • TypeScript 에서는 클래스도 사용자가 만든 타입 중 하나

📖 class

class Person {
	name;
  // 생성자 함수
  constructor(name: string) {
  	this.name = name;
  }
}

// Person obj를 a에 할당
const a = new Person("Kim");

console.log(a);

// class es6부터 추가.
// es5에서 사용할 수 있도록 하기 위해
// example.js 에서 class => function으로 돼있음

📖 constructor & initialize

  • 생성자 함수가 없으면, 디폴트 생성자가 불림
  • 만든 생성자가 하나라도 있으면 디폴트는 사라짐
  • strict 모드에선 프로퍼티를 선언하는 곳 또는 생성자에서 값을 할당해야 함
  • 클래스의 프로퍼티가 정의되어 있지만 값을 대입하지 않으면 undefined
  • 생성자에는 async를 설정할 수 없음
// class 이름은 대문자로 시작.
class Person {
    // tsconfig.json : "strict" = true.
    // "strictNullChecks": true, 
    // "strictPropertyInitialization": true, => age:number에 값이 할당된 게 없음(undefined)
    // 초기값을 설정해주지 않으면 오류발생.
    name: string = "Mark";
    age: number;
    // age!: number => 초기값을 지금 설정안하고 나중에 설정할 때 사용(주의)

    // async constructor (x)
    constructor(age?:number) {
        if (age === undefined) {
            this.age = 20;
        } else {
            this.age = age;
        }
    }

    // JS에서는 오버로딩이 안됨. TS에서는 가능.
}

const p1: Person = new Person(39);
// Perons이 async(비동기)라면 await p1.init();
const p2: Person = new Person(); // 이런 형태 두개 다 지원하려고 한다면 각각의 생성자 생성(오버로딩)
console.log(p1);
// p1.age = 39;
console.log(p1.age);

📖 접근제어자(Access Modifiers)

  • 클래스 내 프로퍼티의 접근 가능성 설정
  • public, private, protected가 있음 (설정하지 않으면 public)
  • 클래스 내부의 모든 곳에(생성자, 프로퍼티, 메서드) 설정 가능
  • pricate로 설정하면 클래스 외부에서 접근 못 함
// 접근제어자 public, private
class Person {
    public name: string = "Mark";
    // 과거에는 private가 없어서 _age 이런 식으로 이름에 표기를 해둠
    // 아직도 관행이 남아서 private는 _를 붙여줌.
    private age: number;

    public constructor(age?:number) {
        if (age === undefined) {
            this.age = 20;
        } else {
            this.age = age;
        }
    }

    public async init() {

    }
}

const p1: Person = new Person(39);
console.log(p1);

📖 initialization in constructor parameters

  • 생성자의 파라미터를 받아서 그 클래스의 프로퍼티로 초기화 하는 방법
// 파라미터에 public을 붙여줌.
class Person {
    public constructor(public name: string, private age: number) {  // age는 외부에서 접근 불가.
    }
}

const p1: Person = new Person("Mark", 39);
console.log(p1);

📖 Getters & Setters

class Person {
    public constructor(private _name: string, private age: number) {
    }
    // 얻어오기.
    get name() {
        // return 필수.
        // console.log('get');
        return this._name + "Lee";
    }
    // 변경하기.
    set name(n:string) {
        // 인자 필수
        // console.log('set');
        // count해서 몇번 set 되었는지 활용할 수 도 있음.
        this._name = n;
    }
}

const p1: Person = new Person("Mark", 39);
console.log(p1.name); // Get, get을 하는 함수 : getter
p1.name = 'Woongjae'; // Set, set을 하는 함수 : setter
console.log(p1.name);

readonly properties

  • set은 할 수 없고 get만 가능한 메서드
  • readonly : 수정을 할 수 없게끔 막아놓을 때 사용
class Person {
    public readonly name: string = 'Mark';
    private readonly country: string;
    public constructor(private _name: string, private age: number) {
        // constructor 안에서만 this로 값 세팅 가능.
        this.country = "Korea";
    }

    hello() {
        // readonly이고 private이므로 수정 불가.
        // this.country = "China";
    }
}

const p1: Person = new Person("Mark", 39);
console.log(p1.name); // Get, get을 하는 함수 : getter
// p1.name = 'Woongjae'; // Set, set을 하는 함수 : setter
console.log(p1.name);
profile
개발자가 되고 싶어~~~

0개의 댓글