TypeScript 강의 정리: 고급타입

zeroequaltwo·2022년 10월 6일
0

TS

목록 보기
6/8
post-thumbnail

1. 교차타입(Intersection type)

  • 기존에 정의해놓은 타입을 결합하여 사용하는 타입
  • 아래와 같은 경우 Admin과 Employee에서 정해놓은 프로퍼티가 하나라도 없으면 에러가 난다.
type Admin = {
  name: string;
  privileges: string[];
};

type Employee = {
  name: string;
  startDate: Date;
};

type ElevatedEmployee = Admin & Employee;

const el: ElevatedEmployee = {
  name: "Max",
  privileges: ["create-server"],
  startDate: new Date()
};
  • 인터페이스로도 전환 가능하다.
interface Admin {
  name: string;
  privileges: string[];
};

interface Employee {
  name: string;
  startDate: Date;
};

interface ElevatedEmployee extends Employee, Admin {};

const el: ElevatedEmployee = {
  name: "Max",
  privileges: ["create-server"],
  startDate: new Date()
};
  • 유니언 타입과 쓸 때는 두 타입이 공통적으로 갖고 있는 교집합 타입만 할당 가능하다.
type Combinable = string | number;
type Numeric = number | boolean;

type universal = Combinable & Numeric;  // number 형식이 교집합이므로 number만 할당 가능하다.
// const test: universal = "test";  // number 형식이 아니라는 에러 발생
const test: universal = 10;

2. Type Guard

  • 유니언 타입 사용 시 유연성 커진다는 장점이 있지만 런타임 시 정확한 타입이 뭔지 알아야 하는 (타입을 모르면 에러가 나거나 엉뚱한 결과를 도출하는) 경우도 왕왕 생긴다. 이를 방지하기 위해서 타입가드 라인을 넣어 의도한 결과만 도출될 수 있게끔 한다.
type Combinable = string | number;

function add(a: Combinable, b: Combinable) {
  if (typeof a === "string" || typeof b === "string") {  // typeof 타입가드
    return a.toString() + b.toString();
  }
  return a + b;
}
  • 커스텀 타입은 기본적으로 제공되는 데이터 타입과는 다르게 typeof로 확인이 불가능하다. 따라서 커스텀 타입 중 어떤 타입에 맞는지 확인하고 싶을 때는 "프로퍼티명" in 확인하고 싶은 변수를 쓰면 쉽게 확인할 수 있다.
type Admin = {
  name: string;
  privileges: string[];
};

type Employee = {
  name: string;
  startDate: Date;
};

type UnknownEmployee = Admin | Employee;

const el: Admin = {
  name: "Max",
  privileges: ["create-server"],
};

function printEmployeeInfo(emp: UnknownEmployee) {
  console.log(`Name: ${emp.name}`);

  // if(typeof emp === "Employee") -> 불가능하다.
  if ("privileges" in emp) {
    console.log(`Privileges: ${emp.privileges}`);
  }
  if ("startDate" in emp) {
    console.log(`StartDate: ${emp.startDate}`);
  }
}

printEmployeeInfo(el);  //Name: Max Privileges: create-server
  • 클래스로 작업하는 경우 instanceof 타입가드도 사용 가능하다. 말그대로 어떤 클래스의 인스턴스인지 확인하는 역할을 하는데 이는 JS에서 제공하는 기능이다.
class Car {
  drive() {
    console.log("Driving a car...");
  }
}

class Truck {
  drive() {
    console.log("Driving a truck...");
  }

  loadCargo(amount: number) {
    console.log("Loading cargo " + amount);
  }
}

type Vehicle = Car | Truck;

const v1 = new Car();
const v2 = new Truck();

function useVehicle(vehicle: Vehicle) {
  vehicle.drive();
  if (vehicle instanceof Truck) {
    vehicle.loadCargo(100);
  }
}

useVehicle(v1);  // Driving a car...
useVehicle(v2);  // Driving a truck... / Loading cargo 100

3. 구별된 유니언(Discriminated union)

  • 유니언 타입이나 객체로 작업할 때 타입가드를 쉽게 구현할 수 있게 해주는 패턴
  • 각 타입마다 리터럴 타입을 갖는 공통의 프로퍼티를 만들어두고 사용하면 편하게 타입을 구별할 수 있다. 또한 오타도 바로 감지하기 때문에 오타의 위험에서도 벗어날 수 있다.
interface Bird {
  type: "bird";  // 공통의 리터럴 타입 프로퍼티
  flyingSpeed: number;
}

interface Horse {
  type: "horse";  // 공통의 리터럴 타입 프로퍼티
  runningSpeed: number;
}

type Animal = Bird | Horse;

function moveAnimal(animal: Animal) {
  let speed;
  switch (animal.type) {
    case "bird":
      speed = animal.flyingSpeed;
      break;
    case "horse":
      speed = animal.runningSpeed;
      break;
  }
  console.log(`Moving at speed: ${speed}`);
}

// moveAnimal({type:"bird", runningSpeed: 5})  // Bird 형식에서는 runningSpeed를 쓸 수 없다고 에러남
moveAnimal({ type: "bird", flyingSpeed: 5 });  // Moving at speed: 5

4. Type casting

  • TS가 직접 감지하지 못 하거나 너무 크게 추론해버린 타입의 값을 개발자가 직접 알려주는 것을 의미한다.
// TS가 inputElement가 input태그라는 것을 추론 못하고 HTMLElement || null 이라고 인식한다.
const inputElement = document.getElementById("user_input"); 
// TS는 inputElement의 존재를 확신하지 못하기 때문에 inputElement에서부터 에러가 발생한다.
inputElement.value = "Hi";  

▽▽▽

// 개발자가 null로 반환하지 않겠다고 정해줘도(!) HTMLElement에는 value라는 속성이 없다고 에러가 난다.
const inputElement = document.getElementById("user_input")!;
inputElement.value = "Hi";

▽▽▽

// 개발자가 inputElement라고 타입을 지정해주면 더이상 에러가 나지 않는다.
const inputElement = <HTMLInputElement>document.getElementById("user_input")!;
inputElement.value = "Hi";

OR

// 리액트에서 볼 수 있는 JSX와의 충돌의 막기 위해 아래와 같이 표기도 가능하다. 
const inputElement = document.getElementById("user_input")! as HTMLInputElement;
inputElement.value = "Hi";

▽▽▽

// null을 반환할 가능성이 있다면 아래와 같이 작성한다.
const inputElement = document.getElementById("user_input");

if (inputElement) {
  // HTMLInputElement과 value가 붙으면 안되니까 ()로 감싼다.
  (inputElement as HTMLInputElement).value = "Hi";  
}

5. Index signature

  • { [Key: T]: U } 형식으로 객체가 여러 Key를 가질 수 있으며, Key와 매핑되는 Value를 가지는 경우 사용한다.
    참고: 인덱스 시그니처
  • 객체를 생성할 내용을 미리 알 수 없고 그때그때 정해야 하는 경우에 주로 사용한다.
interface ErrorContainer {
  [prop: string]: string;
}

const errorBag = {
  email: "Not a valid email",
  username: "Must start with a capital character"
}

6. Function overload

  • 동일한 함수에 대해 여러 함수 시그니처를 정의할 수 있는 기능으로, 다양한 타입의 파라미터를 지닌 함수를 여러가지 경우의 수로 쪼개서 덧붙인다.
  • TS가 자체적으로 리턴 타입을 추론하지 못 할 때 유용하다.
type Combinable = string | number;

function add(a: Combinable, b: Combinable) {
  if (typeof a === "string" || typeof b === "string") {
    return a.toString() +" "+ b.toString();
  }
  return a + b;
}

const actress1 = add("Keira", "Knightley");
actress1.split(" ");  // TS는 Combinable을 타입으로 받아들이기 때문에 string이라고 확신하지 못해서 에러가 난다.

▽▽▽

type Combinable = string | number;

function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: Combinable, b: Combinable) {
  if (typeof a === "string" || typeof b === "string") {
    return a.toString() + " " + b.toString();
  }
  return a + b;
}

const actress1 = add("Keira", "Knightley");
const splitedName = actress1.split(" "); // TS는 Combinable을 타입으로 받아들이기 때문에 string이라고 확신하지 못해서 에러가 난다.
console.log(splitedName);  // ['Keira', 'Knightley']

7. Optinal chaining

  • 중첩된 객체를 다룰 때 중첩 단계별로 오류 없이 안전하게 접근할 수 있게 해주는 기능이다.
  • 있는지 없는지 모르겠는 key 옆에 물음표+온점(?.)를 붙여주면 된다.
const userData = {
  id: 1,
  name: "Max",
  //job: { title: "CEO", description: "Own a company" },
};

console.log(userData.job?.description);

8. null 병합(Nullish coalescing)

  • null이나 undefiend 값이 나오면 대신할 값을 미리 정해두는 기능
  • 물음표 2개(??)로 구현한다.
  • 프론트랑 백이 데이터 주고받을 때 유용하게 쓸 수 있다.
const userInput = null;
console.log(userInput ?? "No data");  // No data
profile
나로 인해 0=2가 성립한다.

0개의 댓글