[TS] Section6 고급 타입

lim1313·2022년 2월 6일
0

TIL

목록 보기
13/22

🍉 인터섹션 타입

인터섹션 타입을 이용해 “여러 경우에 모두 해당”하는 타입을 표현할 수 있다.

객체

A & B 타입의 값은 A 타입에도, B 타입에도 할당 가능해야 한다. 만약 A와 B 모두 객체 타입이라면 A & B 타입의 객체는 A와 B 타입 각각에 정의된 속성 모두를 가져야 한다.

type Admin = {
  name: string;
  privileges: string[];
};

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

type ElevatedEmployee = Admin & Employee;

const e1: ElevatedEmployee = {
  name: 'max',
  privileges: ['created-server'],
  startDate: new Date(),
};

type 대신 아래와 같이 interface를 사용할 수 있다.
하지만 조금 더 짧은 type이 더 선호된다.

interface Admin {
  name: string;
  privileges: string[];
}

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

type ElevatedEmployee = Admin & Employee;

extends

위와 완전히 동일한 결과값을 같는다.

interface ElevatedEmployee extends Admin, Employee {}

객체 이외의 타입

A와 B 타입 모두에 할당 가능한 값이여야 한다.

type Combinable = string | number;
type Numeric = number | boolean;

type Universal = Combinalbe & Numeric;

let num: Universal = 1;

이 경우 number 타입이 공통적으로 할당되어 있기 때문에 Universal은 number 타입이 된다.

하지만 다음의 경우는 어떤 값도 만족하지 않는 인터섹션 타입이다.

type Infeasible = string & number

문자열인 동시에 숫자인 값은 존재하지 않으므로, 위 Infeasible 타입은 실제로는 어떤 값도 가질 수 없다.


🍉 타입 가드에 대한 추가 정보

인터섹션 타입이 런타입 시 정확히 어떤 타입을 얻게 될지 알아야 하는 경우가 많다.

타입가드는 특정 속성이나 메소드를 사용하기 전에 그것이 존재하는지 확인하거나 타입을 사용하기 전에 이 타입으로 어떤 작업을 수행할 수 있는지를 확인하는 개념 또는 방식을 나타낸다.

typeof 를 활용한 타입가드

유니온 타입이 지닌 유연성을 활용할 수 있게 해주며 런타임 시 코드가 정확하게 작동하게 해 준다.

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

in 을 활용한 타입가드

type UnknownEmployee = Employee | Admin;

function printEmployeeInformatino(emp: UnknownEmployee) {
  // UnknownEmployee의 Employee, Admin 모두 name 속성이 있기 때문에 OK
  console.log('Name: ' + emp.name);
  
  // UnknownEmployee의 Employee에는 privileges의 속성이 없기 때문에 Error
  console.log('Privileges: ' + emp.privileges); //=> Error

  // in을 활용한 타입가드
  // emp에 privileges 속성이 있는지 확인
  if ('privileges' in emp) {
    console.log('Privileges: ' + emp.privileges);
  }
}

class 에서의 instanceof를 활용한 타입가드

class Car {
  drive() {
    console.log('Driving...');
  }
}

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 ('loadCargo' in vehicle) {
    vehicle.loadCargo(1000);
  }
	
  // instanceof를 활용한 타입가드 
  if (vehicle instanceof Truck) {
    vehicle.loadCargo(1000);
  }
}

useVehicle(v1);
useVehicle(v2);

🍉 Distriminated 구별된 유니언

type 혹은 kind 라는 객체를 설명하는 속성을 switch문에 사용하여 완전한 타입 안전성을 갖추고, 객체에 어떤 속성을 사용할 수 있는지 파악할 수 있다.

실제 존재하는 속성을 사용하여 어떤 유형의 객체와 작업하고 있는지 확인할 수 있다.
또한 오타의 위험을 없애준다.

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;
  }

  console.log('Moving at speed : ' + speed);
}

moveAnimal({ type: 'bird', flyingSpeed: 10 });

🍉 형 변환 type casting

<> 사용하여 형 변환

const userInputElement = <HTMLInputElement>document.getElementById('user-input')!;

userInputElement.value = 'HI, THERE!';

as 사용하여 형 변환

react의 JSX 구문과 충돌하는 것을 막기 위해서 as를 사용한다.

const userInputElement = document.getElementById('user-input')! as HTMLInputElement;

userInputElement.value = 'HI, THERE!';
function add2(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}

const result = add('max', 'schwarz') as string;
result.split(' ');

! (느낌표)

느낌표를 사용하여 느낌표 앞의 표현식을 null로 반환하지 않겠다고 타입스크립트에게 인식시킬 수 있다.

null을 반환할 수 있는 dom에서 이것이 null을 반환하지 않을 것이라는 서을 안다면 느낌표를 사용할 수 있다.

const userInputElement = <HTMLInputElement>document.getElementById('user-input')!;

null을 반환하지 않을 것이라는 확신이 들지 않는 경우 if문을 사용하면 된다.
하지만, 이 경우에는 형 변환을 제거해야 한다. 여기서 형 변환을 하면 null이 반환될 것이라는 확신이 없음에도 타입스크립트에게 null이 되지 않는다는 것을 알려주는 것이 되기 때문이다.

때문에 아래의 예시처럼 value에 접근해야 하는 경우 아래처럼 as HTMLInputElement를 추가하여 형 변환을 수행하면 된다.

const userInputElement = document.getElementById('user-input');

if (userInputElement) {
  (userInputElement as HTMLInputElement).value = 'HI, THERE!';
}

🍉 인덱스 시그니처 index signature

인덱스 시그니처는 [key:type]: type 형식으로 객체가 여러 key를 가질 수 있으며, key와 매핑되는 value를 가지는 경우 사용한다.
key와 value의 타입을 정확하게 명시해야 하는 경우 사용할 수 있다.

interface ErrorContainer {
  email: string;
  [prop: string]: string;
}

const errorBag: ErrorContainer = {
  email: 'Not a valid email!',
  address: 'seoul',
  username: 'capital',
};

🍉 함수 오버로드

매개변수가 다르며 이름이 동일한 함수를 오버로딩이라고 한다.

여러 개의 함수 선언과 하나의 함수 구현이 있는 함수가 존재한다.
함수에서 지원할 수 있는 다양한 조합에 대해 어떤 것이 반환되는지 명확하게 알 수 있다.

// 함수 선언 
function add2(a: string, b: string): string;
function add2(a: number, b: number): number;
function add2(a: number, b: string): string;
function add2(a: string, b: number): string;


// 함수 구현
function add2(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}

const result = add2('max', ' schwarz');
result.split(' ');

🍉 선택적 체이닝

HTTP 요청의 경우에서 객체의 일부 속성이 설정되어 있는지 또는 정의되지 않았는지 확실히 알 수 없는 중첩된 데이터로 구조화 작업을 수행하는 경우가 있다.

다음과 같이 &&을 사용하여 런타임 에러는 피할 수 있다.

const fetchedUserData = {
  id: 'u1',
  name: 'max',
  job: { title: 'ceo', description: 'my company' },
};

console.log(fetchedUserData.job && fetchedUserData.job.title);

하지만 ts 더 나은 방법을 사용할 수 있다. 이 기능은 3.7버전 이상에서만 지원된다.

요소 다음에 물음표를 추가하면 해당 부분의 존재여부를 알려준다. 물음표 앞의 요소가 정의되지 않았다면 그 이후에는 해당 요소에 접근하지 않으므로 런타임 에러가 발생하지 않지만 에러가 지속되지 않는다.

const fetchedUserData = {
  id: 'u1',
  name: 'max',
  job: { title: 'ceo', description: 'my company' },
};

//job이 정의되어 있는 경우 title에 접근할 수 있다.
console.log(fetchedUserData?.job?.title);

🍉 Null 병합

어떤 데이터가 null, undefined(빈 문자열, 0은 제외)일 경우에만 폴백을 사용해야 한다는 의미이다.


const userInput = '';
const storeData = userInput || 'Default';
const storeData2 = userInput ?? 'Default';

console.log(storeData); //=> Default
console.log(storeData2); //=> ''(빈문자열)
profile
start coding

0개의 댓글