230608.til

Universe·2023년 6월 9일
0
post-custom-banner

Union type

여러 타입 중 하나일 수 있는 값을 나타낸다. (or)
'|' 연산자를 사용하여 정의할 수 있다.
함수의 인수를 처리하거나 다양한 타입의 값을 반환할 수 있는 함수를 만드는 데 용이하다.
할당할 수 있는 값을 여러가지로 확장할 수 있기 때문에 코드의 유연성이 좋아지고,
어떠한 값들을 할당할 수 있는지 명시적으로 지정하기에 예측가능한 함수를 만드는데 효과적이다.

function myFunction(input: string | number){
	...
}

위의 예시는 함수의 인자로 string 혹은 number가 할당될 수 있음을 명시한다.
유니온 타입을 사용할 때, "어떠한 값이 전달되었는지" 에 대한 후처리를 해주어야 함에 주의.
예를들어,


function myFunction(input: string|number) {
  return input * 2; //산술 연산의 왼쪽은 'any', 'number', 'bigint' 또는 열거형 형식이어야 합니다.ts(2362)

}

위와 같이 유니온 타입을 지정하면 string 타입은 산술 연산을 진행할 수 없다는 에러를 출력한다.
이런 경우, Type Guard 를 설정하여 전달받은 값에 대한 후처리를 해주어야 한다.


function myFunction(input: string|number):string|number{
  if(typeof input === "string"){
    return input + input
  } else {
    return input * 2
  }
}

Type guard ?

특정 코드 블록 내에서 변수의 타입을 좁히거나 확인하는 방법.
보통 조건문, typeof 연산자, instanceof 연산자를 사용해 구현한다.
타입 가드를 사용하면 특정 코드블럭 내에서 변수의 타입을 구체적으로 구분하여
타입에 따른 다른 동작 등을 정의할 수 있다.
위의 예시에서 설명한 유니온 타입의 후처리도 이와 같은 맥락이라고 볼 수 있다.

Intersection Type

여러 타입을 하나로 결합하는 방법. (and)
'&' 연산자를 사용하여 정의할 수 있다.
기본적으로 두 가지 이상의 타입을 모두 만족하는 새로운 타입을 생성하는 역할을 한다.
기존의 타입들을 재사용한다는 측면에서 재사용성과 확장성에 이점을 가지고 있지만,
과도하게 사용했을 때 코드를 이해하기 어려워지며
아래와 같은 문제점을 가지고 있다.

type A = {
  A: number;
  B: string;
}

type B = {
  B: number;
  C: string;
}

type C = A | B
type D = A & B

const obj1: C = {
  A:10,
  B:"hi",
  C:'hi'
}

const obj2: D ={
  A:10,
  B:123,//'number' 형식은 'never' 형식에 할당할 수 없습니다.ts(2322)
  C:"hi"
}

type D 는 type A와 type B 의 intersection 타입이다.
intersection 타입은 "교차되는 조건에 대한 참" 이어야 하므로
위의 B 속성의 경우 "string 타입임과 동시에 number 타입" 이어야 한다.
그런 이상한 값은 이 세상에 존재하지 않으므로 typescript 자체에서 never 타입이라고
오류를 반환하는 모습.
따라서, intersection 타입에서는 교차되는 타입이 같은 경우에만 사용할 수 있다.
아래의 예시를 보면 조금 더 명확하게 잘못된 점을 찾아볼 수 있다.

type invalidType = string & boolean & number;
// 문자이면서 불리언이면서 숫자인 어떤 타입 ?

반대로 type C 에서는 B 속성에 number, string 어느 타입을 할당해도 오류를 반환하지 않는다.

class

typescript 는 javascript 의 class 를 확장하여 타입의 안정성을 제공한다.
기본적인 구조는 다음과 같다.

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
  • public, private, protected: 접근 제어자.
    클래스의 메소드나 속성에 대한 접근을 제어한다.
    public은 어디서나 접근 가능하며,
    private은 클래스 내부에서만 접근 가능하고,
    protected은 클래스 내부와 그 하위 클래스에서만 접근이 가능하다.
class Person {
  public name: string;
  private age: number;

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

const person = new Person("John", 30);
console.log(person.age); // 오류: 'age' 속성은 private이므로 'Person' 클래스 외부에서 접근할 수 없습니다.
  • readonly: 한 번 할당되면 변경할 수 없는 속성. 읽기전용.
class Person {
  readonly name: string;

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

const person = new Person("John");
person.name = "Mike"; // 오류: 'name'은 읽기 전용 속성입니다.
  • static: 클래스 자체에 속성이나 메소드를 정의.
class MyClass {
  static myStaticProperty = "some value";

  static myStaticMethod() {
    return MyClass.myStaticProperty;
  }
}

const myInstance = new MyClass();
console.log(myInstance.myStaticProperty); // 오류: 'myStaticProperty' 속성은 클래스 'MyClass'의 인스턴스에 존재하지 않습니다.
  • extends: 클래스간 상속 기능.
class Person {
  constructor(public name: string) {}
}

class Employee extends Person {
  constructor(public name: string, public employeeId: number) {
    super(name);
  }
}
  • abstract: 추상 클래스를 정의.
    인스턴스를 직접 생성할 수 없으며, 하위 클래스를 통해 구체화해야 하는 클래스를 정의.
abstract class Shape {
  abstract area(): number;
}

const shape = new Shape(); // 오류: 추상 클래스의 인스턴스를 만들

Generics

"재사용이 가능한 코드를 만들기 위한 타입의 추상화"
마치 타입을 파라미터 처럼 사용할 수 있다.
이러한 이유 때문에 typescript 에서 제네릭을 가장 많이 사용하는 대표적인 예시는
array 와 promise 이다.
배열의 경우, 어떤 타입의 요소를 담을 수 있는지 표현할 수 있으며
프로미스의 경우, 비동기 작업이 성공했을 때 어떤 타입의 결과를 반환할 지 표현할 수 있다.
기초적인 사용법은 다음과 같다.

function identity<T>(value: T): T {
  return value;
}

let number = identity<number>(123);
let string = identity<string>('abc');

let error = identity<number>('abc'); // 'abc' 타입은 number에 할당할 수 없습니다.
profile
Always, we are friend 🧡
post-custom-banner

0개의 댓글