728x90

Intersection Type (교차 타입)

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

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

type ElevatedEmployee = Admin & Employee;

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

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

type Universal = Combinable & Numeric;

const u: Universal = 10;
const u2: Universal = false; // Error
const u3: Unviersal = '10'; // Error

 

custom type을 선언하여 & 연산자를 사용하여 새로운 type을 만들 때는 합집합이 됩니다.

하지만 union type 을 사용하여  string | number와  number | boolean의 타입을 & 연산자로

하나의 타입으로 만들 때는 교집합으로 이루어 집니다.

 

 

Initial type 이 number 인 것을 확인할 수 있습니다.

 

TypeGuard - 런타임에서 조금 더 안전하게 타입 사용하기

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

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

type ElevatedEmployee = Admin & Employee;

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

type UnknownEmployee = Employee | Admin;

function printEmployeeInformation(emp: UnknownEmployee) {
    console.log('Name: ' + emp.name);
    // 사용자 정의 유형은 Typescript 에만 존재함으로 이렇게 비교할 수 없음
    // if (typeof emp === 'Employee')

    // privileges 가 emp 의 property 인지 확인한다.
    if ('privileges' in emp) {
        console.log('Privileges: ' + emp.privileges);
    }

    if ('startDate' in emp) {
        console.log('Start Date: ' + emp.startDate);
    }
}

printEmployeeInformation(e1);

 

관리자 타입과 일반 직원 타입 둘 다 가질 수 있는 UnknownEmployee 타입이 있습니다. 

type의 속성들을 출력하는 함수를 만들었는데 관리자 타입인지 직원 타입인지 를 컴파일에서 알 수 없기 때문에

컴파일이 되어도 런타임에서 에러가 발생할 수 있습니다.

이를 사전에 방지하고자 type guard를 사용해 좀 더 안전하게 코딩을 하려고 합니다.

 

if (typeof emp === 'Employee') // X
if (typeof emp === 'object') // O

이렇게 파라미터 값을 직접 typeof로 비교하면 되지 않을까 싶지만 커스텀 타입은 javascript 에는 존재하기 않기 때문에 비교할 수 없습니다. 

 

그렇다면 어떻게 비교해야 할까?

 

// privileges 가 emp 의 property 인지 확인한다.
if ('privileges' in emp) {
	console.log('Privileges: ' + emp.privileges);
}

 

in 연산자를 사용하여 privileges라는 key 가 emp 안에 존재하는지 여부를 체크합니다.

 

하지만 string으로 검사를 하다 보면 사람이 실수를 할 수 도 있고 2% 부족한 감이 없지 않아 있습니다.

 

명시적으로 type 검사하기

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 (vehicle instanceof Truck) {
        vehicle.loadCargo(1000);
    }
}
useVehicle(v1);
useVehicle(v2);

 

instanceof라는 keyword를 사용하여 런타임 환경에서 v1이라는 인스턴스가 어떤 클래스로 만들어졌는지를 검사합니다.

javascript 에도 존재하기 때문에 이것이 가능합니다.

 

Union Type 식별하기

 

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;
    default:
      break;
  }
  console.log("Moving at speed: " + speed);
}

 

interface로 구현된 타입들의 Union Type 은 Class처럼 javascript 에는 존재하지 않는 타입의 형태 이기 때문에

if (animal instanceof Bird) {}

와 같이 인스턴스로 구분할 수가 없다.

 

그렇다고 string으로 구분을 하자니 이건 우리가 지양하는 타입이니 좀 더 좋은 방법이 없을까?

 

위에서 보다시피 type이라는 key에 리터럴 한 값을 집어넣었다. 이건 tyepscript에서 말하는 type 은 아니지만 런타임 환경에서도 if문 또는 switch 문에서 사용할 수 있는 type 은 확실하다.

 

typescript의 타입 추론 덕분에

case 'bird':
	// animal.runningSpeed 가 오면 불만을 표출한다.

 

case 'bird'라는 스코프 안에서는 animal의 type 이 Bird라는 것을 추론하여 Horse의 property 가 오면 에러를 발생시킨다.

이것이 typescript의 장점인 것 같다.

 

Type casting

 

typescript로 개발을 하다 보면 우린 분명 이 것에 대한 type을 알고 있는데 type 추론은 이것을 알지 못할 때가 있다.

const inputTag = document.getElementById("user-input")!;

inputTag.value = "user1"; // Error

 

type 추론은 이것을 HTMLElement로 생각한다. 개발자들의 의도는 이것은 분명 input tag를 의도하고 쓴 거 겠지만 아쉽게도 추론에 실패했다.

이렇게 된 경우 타입 캐스팅으로 타입을 명시적으로 변환해야 한다.

타입 캐스팅엔  두 가지 방법이 있다.

 

const inputTag = <HTMLInputElement>document.getElementById("user-input")!;
const inputTag2 = document.getElementById("user-input")! as HTMLInputElement;
inputTag.value = "user1";
inputTag2.value = "user2";

꺽쇠를 사용해 타입 캐스팅하는 방법과 as를 사용해 타입 캐스팅을 하는 방법이다.

 

요기서! 느낌표의 역할이 뭐냐고 물어보시는 분들이 있을 수도 있다.

user-input의 id를 가진 element를 가져올 때 이것은 null 일 수도 있고 inputelement 일수도 있다.

그래서 우리는! 를 달아 줌으로써 컴파일러에게 이건 null 이 아니라고 말해 주는 것이다.

 

if 문 사이에 넣는 것과 동일 한 효과다.

728x90

'TypeScript' 카테고리의 다른 글

Typescript Generic programing  (0) 2021.11.29
Advanced Typescript 2  (0) 2021.11.29
Typescript Interface  (0) 2021.11.28
Typescript Class  (0) 2021.11.27
typeorm database migration  (0) 2021.06.24