// type
type Admin = {
name: string;
privileges: string[];
};
type Employee = {
name: string;
startDate: Date;
};
type tmp = Admin & Employee;
type tmp = Admin | Employee;
// interface
interface Admin {
name: string;
privileges: string[];
}
interface Employee {
name: string;
startDate: number;
}
type ElevatedEmployee = Admin & Employee;
// or
// interface ElevatedEmployee extends Admin, Employee {}
type ElevatedEmployee = Admin | Employee;
이 경우 A, B, C에 해당하는 모든 type이 객체 초기화, 할당시 구현이 되어야한다.
이 경우에는 A,B 또는 B,C 또는 A,B,C 모든 타입들이 객체의 초기화, 할당시 구현되어야한다.
type Combinable = string | number;
type Numeric = number | boolean;
type Universal = Combinable & Numeric;
//=> type Universal = number
type Universal2 = Combinable | Numeric;
//=> type Universal2 = string | number | boolean
타입가드는 유니언 타입을 돕는다. 유니언을 통해 유연성을 얻는 것은 좋지만, 런타임 시 정확히 어떤 타입을 얻게 될지 알아야하는 경우가 많기 때문이다. type guard의 종류가 몇가지 있는데 이를 아래에서 소개하도록 하겠다.
아래의 코드에서 만약 하나의 인자라도 string타입이 들어오게 되면 작동할 로직에 대해 정하는 조건문이 있는데 이 조건문의 조건부가 type guard이다.
type Uniersal = number | string;
function add(a: Uniersal, b: Uniersal) {
if (typeof a === "string" || typeof b === "string") {
// 조건문 부분이 type guard
return a.toString() + b.toString();
}
return a + b;
}
우리가 커스텀 타입으로 지정한 이름은 JS의 type 키워드로 검사할 수 없다. 그래서 커스텀 타입의 구성요소(propety, method)의 존재 유무를 if문을 통해 체크하거나 in keyword를 이용한 타입가드를 사용할 수 있다.
첫번째 방법의 경우 TS에서 커스텀 탑입으로 정의한 객체의 구성에 접근하지 못해 에러를 띄운다.
아래의 경우 JS의 in keyword를 이용해 각 method나 property가 존재하는지 여부를 따져 실행한다.
function printEmpInfo(e: emp) {
if ("privileges" in e) {
console.log("privileges: " + e.privileges);
}
if ("startDate" in e) {
console.log("startDate: " + e.startDate);
}
}
instanceof를 이용해 어떤 class의 instance인지 여부를 체크하는 타입가드도 있다. 아래와 같이 if문에서 체크한다.
중요! instanceof 키워드의 경우 JS의 문법이기 때문에 TS의 문법인 interface를 체크하지 못하니 이를 유의해야한다.
class Car {
driving() {
console.log("driving car...!");
}
}
class Truck {
driving() {
console.log("driving Truck Yeah...!");
}
load() {
console.log("loading stuff!");
}
}
type Vehicle = Car | Truck;
function carAction(v: Vehicle) {
v.driving();
if (v instanceof Truck) {
v.load();
}
}
const trk = new Truck();
carAction(trk);
유니언 타입의 타입가드를 도와주는 discriminated union이 있다. 유니언 타입의 타입가드를 쉽게 구현할 수 있게 해주는 패턴이다. 객체 타입에도 사용 가능하다. 유니언 타입이 많아지는 경우 타입가드를 위한 if문이 그만큼 많아지는데 이를 작성함에 따라 오타나 타입의 개수만큼 if문이 많아지는 비효율이 발생한다. discriminated union은 이를 해결할 수 있는 패턴이다.
인터페이스와 클래스에 사용가능하다. 개요는 클래스와 인터페이스 선언 당시 서로를 구분하는 keyword나 type을 리터럴로 주어 각 클래스를 switch문을 통해 구분할 수 있는 정보를 남기는 것이다.
// discriminated union
interface Bird {
type: "bird";
flyingSpeed: number;
}
interface Horse {
type: "horse";
runningSpeed: number;
}
type Animal = Bird | Horse;
function printAnimalSpeed(animal: Animal) {
let speed;
switch (animal.type) {
case "bird":
speed = animal.flyingSpeed;
break;
case "horse":
speed = animal.runningSpeed;
break;
}
console.log("This animal's speed is " + speed);
}
const nonnie: Bird = { type: "bird", flyingSpeed: 1000000 };
printAnimalSpeed(nonnie);
// 아래 두 형변환(typecasting)은 동등(equivalent)하다.
const btnById1 = <HTMLButtonElement>document.getElementById("button-yo")!;
//react에서는 컴포넌트 표기법과 충돌할 여지가 있다.
const btnById2 = document.getElementById("button-yo")! as HTMLButtonElement;
typecasting은 타입스크립트가 직접 감지하지 못하는 특접 타입의 값을 타입스크립트에게 알려주는 역할을 함. 예를 들어 DOM에 접근하는 것이다. tag를 통해 DOM element에 접근할 시 그 element가 어떤 종류인지 추론이 가능하지만, id를 통해 JS에서 HTML DOM에 접근하면 TS에서 어떤 tag인지는 판별하지 못한다. 동시에 IDE에서 method나 property에 대한 추천도 하지 못하게 된다. 이런 문제들을 위의 타입캐스팅이 해결해준다.
const btnByTag = document.querySelector("button");
// const btnByTag: HTMLButtonElement | null
// => HTMLButtonElement
const btnById = document.getElementById("button-yo");
// const btnById: HTMLElement | null
// => HTMLElement, element의 종류는 추론 불가
class나 interface 내에 앞으로 만들어질 property의 타입을 제한할 수 있다.
// index type
interface ErrorContainer {
[prop: string]: string;
//id: number; => !!! ERROR !!!
}
함수 오버로딩은 함수 선언시 같은 이름을 가진 함수를 parameter에 대한 정의를 다르게 해 함수 호출시 입력된 인자의 type이나 개수에 따라 다른 기능을 하도록 할 수 있게하는 기능이다.
// function overloading
type Combinable = number | string;
function add(a: number, b: number): number;
function add(a: number, b: string): string;
function add(a: string, b: number): string;
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;
}