기존 타입을 대체하지 않으면서 기존 타입에 새로운 필드를 추가
&
사용
유니온타입을 인터섹션하면?
type Combinable = string | number; type Numeric = number | boolean; type Universal = Combinable & Numeric;
- Universal은 number타입이다 => 공통 부분만 해당됨
type Combinable = string | number;
type Numeric = number | boolean;
function add(a:Combinable, b: Combinable) {
if(typeof a === 'string' || typeof b ==='string'){ //둘중에 하나는 무조건 string => 타입가드
return a.toString() + b.toString();
}
return a+b; //둘다 string이 아니란것을 만족함
}
1. 사용자 정의 타입을 타입가드하기
type UnknownEmployee = Employee | Admin; function printEmloyeeInfo(emp: UnknownEmployee) { console.log('Name ' + emp.name); if ('privileges' in emp) { console.log('Privileges : ' + emp.privileges); } if ('startDate' in emp) { console.log('StartDate : ' + emp.startDate); } }
typeof emp === 'Employee'
와 같은것은 불가능하기 때문에in
을 사용한다
2. 클래스를 타입가드하기
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(5); } */ if(vehicle instanceof Truck){ vehicle.loadCargo(5);' } }
3. 인터페이스를 타입가드하기
- 인터페이스는 instanceof를 사용할 수 없다
- 구별된 유니온을 만들자
interface Bird { type: 'bird'; //type이라고 지정할 필요x 그냥 구분을 위한 이름이다 flyingSpeed: number; } interface Horse { type: 'horse'; runningSpeed: number; } type Animal = Bird | Horse; function moveAnimal(animal: Animal) { /* if('flyingSpeed' in animal){ console.log('Moving with Speed: ' + animal.flyingSpeed); } */ switch (animal.type) { case 'bird': console.log('Moving with Speed: ' + animal.flyingSpeed); return; case 'horse': console.log('Moving with Speed: ' + animal.runningSpeed); return; default: return; } }
동일한 이름에 매개변수만 다른 여러번전의 함수를 만드는 것 (화살표 함수 x)
type Combinable = string | number;
type Numeric = number | boolean;
type Universal = Combinable & Numeric;
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;
}
const result1 = add('Max', 'Schwarz');
result1.split(' ');
const result2 = add(2, 1);
// result2.split(' ') => number라 split불가
접근하는 객체의 프로퍼티가 null 또는 undefined일 수 있는 optional property인 경우 if 문을 사용하지 않고 넘어가게 하는 체이닝 방법이다.
const fetchUserData = {
id: 'u1',
name: 'max',
// job: { title: 'CEO', description: 'My Own Company' },
};
//만약 데이터를 가져올때 이 데이터가 값이 없을수도 있고 못가져올 수도 있다
console.log(fetchUserData?.job?.title);
null, undefined를 제외한 falsy 값을 그대로 리턴 하고 싶은 경우 사용한다.
??
사용
const userInput = '';
/* const storedData = userInput || 'DEFAULT';
console.log(storedData); //DEFAULT
*/
const storedData = userInput ?? 'DEFAULT';
console.log(storedData); //빈문자열
타입을 마치 함수의 파라미터처럼 사용하는 것
function merge<T extends object, U extends object>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
console.log(merge({ name: 'Max' }, { age: 30 }));
const mergedObj = merge<{ name: string; hobbies: string[] }, { age: number }>(
{ name: 'Max', hobbies: ['Sports'] },
{ age: 30 }
);
특정 메소드나 속성이 있음을 명확히 해주기위해 인터페이스를 사용
interface Lengthy { length: number; } function countAndPrint<T extends Lengthy>(element: T): [T, string] { let decription = 'Got no vlaue.'; if (element.length === 1) { decription = 'Got 1 element.'; } else if (element.length > 1) { decription = 'Got ' + element.length + ' elements.'; } return [element, decription]; } console.log(countAndPrint('Hi there')); console.log(countAndPrint(['Sports', 'Cooking'])); //console.log(countAndPrint(1));
keysof, extends 활용(제약조건)
function extractAndConvert<T extends object, U extends keyof T>( obj: T, key: U ) { return 'Value: ' + obj[key]; } console.log(extractAndConvert({ name: 'MAX' }, 'name')); //console.log(extractAndConvert({ name: 'MAX' }, 'age'));
class DataStorage<T extends string | number | boolean> {
private data: T[] = [];
addItem(item: T) {
this.data.push(item);
}
removeItem(item: T) {
if (this.data.indexOf(item) === -1) {
return;
}
//원시값이 아닌 객체나 배열이 전달되는 경우 실행 불가능, 항상 -1 출력
this.data.splice(this.data.indexOf(item), 1);
}
getItems() {
return [...this.data];
}
}
const textStorage = new DataStorage<string>();
const numberStorage = new DataStorage<number>();
메타프로그래밍이란?
- 개발자가 사용하기 쉬운 도구를 제공하는데 적합
"experimentalDecorators": true
데코레이터 함수를 감싸는 래퍼함수
데코레이터는 하나의 멤버에 동시에 여러개의 데코레이터를 장식할 수 있다
// Size 데코레이터 팩토리
function Size() {
console.log('Size(): 평가됨');
// Size 데코레이터
return function (target: any, prop: string, desc: PropertyDescriptor) {
console.log('Size(): 실행됨');
};
}
// Color 데코레이터 팩토리
function Color() {
console.log('Color(): 평가됨');
// Color 데코레이터
return function (target: any, prop: string, desc: PropertyDescriptor) {
console.log('Color(): 실행됨');
};
}
// Button 클래스 정의
class Button {
// 메서드에 멀티 데코레이터 적용
@Size()
@Color()
isPressed() {}
}
/*
Size(): 평가됨
Color(): 평가됨
Color(): 실행됨
Size(): 실행됨
*/