타입 가드 예시
type SuccessResponse = {
status: true;
data: any;
};
type FailureResponse = {
status: false;
error: Error;
};
type CustomResponse = SuccessResponse | FailureResponse;
declare function getData(): CustomResponse;
const response: CustomResponse = getData();
// status 프로퍼티로 추론하자
if (response.status) {
// status가 true인 경우: SuccessResponse 타입
console.log(response.data);
} else if (response.status === false) {
// status가 false인 경우: FailureResponse 타입
console.log(response.error);
}
좀 더 심화 ↓↓
type SuccessResponse = {
status: true
data: any;
};
type FailureResponse = {
status: false
error: Error;
};
type CustomResponse = SuccessResponse | FailureResponse;
function typeGuard() {
const response = getData();
console.log(response.status); // 💙
if (response.status === false) {
console.log(response.error.message); // 💛
} else if (response.status === true) {
console.log(response.data); // 💛
}
}
function getData(): CustomResponse {
if (Math.random() < 0.5) {
return { status: false, error: new Error('error!') };
}
return {
status: true,
data: 'success!',
};
}
typeGuard();
// Math.random값이 0.5미만이면
// false 💙
// error! 💛
// Math.random값이 0.5이상이면
// true 💙
// success! 💛
인스턴스 instanceof 클래스
형식class Developer {
develop() {
console.log(`I'm working`);
}
}
class Designer {
design() {
console.log(`I'm working`);
}
}
const work = (worker: Developer | Designer) => {
// worker인스턴스가 Desinger의 객체일 때
if (worker instanceof Designer) {
worker.design();
} else if (worker instanceof Developer) {
worker.develop();
}
};
type of 데이터 === ‘number’
형식const add = (arg?: number) => {
if (typeof arg === "undefined") {
return 0;
}
return arg + arg;
};
문자열 A in Object
형식type Human = {
think: () => void;
};
type Dog = {
tail: string;
bark: () => void;
};
declare function getEliceType(): Human | Dog;
const elice = getEliceType();
// elice 오브젝트의 key에 tail이 있는 경우
if ("tail" in elice) {
elice.bark();
} else {
elice.think();
}
※ 속도 측면: switch >> if . 따라서 switch 사용 권장
type Action = "click" | "hover" | "scroll";
const doPhotoAction = (action: Action) => {
// 1번쨰 방법: switch 이용
switch (action) {
case "click":
showPhoto();
break;
case "hover":
zoomInPhoto();
break;
case "scroll":
loadPhotoMore();
break;
}
};
const doPhotoAction2 = (action: Action) => {
// 2번째 방법: === 연산자(if문) 이용
if (action === "click") {
showPhoto();
} else if (action === "hover") {
zoomInPhoto();
} else if (action === "scroll") {
loadPhotoMore();
}
};
sindresorhus/is
이용import is from "@sindresorhus/is";
const getString = (arg?: string) => {
if (is.nullOrUndefined(arg)) {
return "";
}
if (is.emptyStringOrWhitespace(arg)) {
return "";
}
return arg;
};
?.
형식&&
와 ?.
의 차이&&
: falsy(false, null, undefined, ‘’, 0, -0, NaN) 값 체크?.
: null과 undefined만 체크obj?.name
obj가 null, undefined가 아니면 name을 리턴arr?.[0]
arr이 null, undefined가 아니면 첫번째 요소를 리턴func?.()
함수 func이 null, undefined가 아니면 func 함수 호출예시
type Dog = {
tail?: {
softly: () => string;
}
}
function tailSoftly(dog: Dog): string {
return dog.tail?.softly()
}
이때, dog.tail이 만약 null 혹은 undefined인 경우, undefined을 반환.
dog.tail이 null이나 undefined가 아닌 경우, softly 메소드(string값) 반환.
object
type CustomResponse = {
data: any
}
const findData = (response?: CustomResponse) => {
return response?.data
}
response가 null이거나 undefined이면, undefined 반환
그렇지 않으면, data반환
array
type Post = {
comments?: string[]
}
const gestFirstComment = (post: Post) => {
return post.comments?.[0]
}
post.comments가 null이거나 undefined이면, undefined.
그렇지 않으면, 첫번째 요소 반환
type Dog = {
tail?: {
softly: () => string;
}
}
function tailSoftly(dog: Dog): string {
return dog.tail?.softly()
}
// if문
type Tail = {
살랑살랑: () => void;
};
type Human = {
call: (dogName: string) => void;
};
type Dog = {
name: string;
tail?: Tail;
주인?: Human;
};
function petDog(dog: Dog) {
// != null: null 또는undefined가아닌경우
if (dog.주인 != null) {
dog.주인.call(dog.name);
}
if (dog.tail != null) {
dog.tail.살랑살랑();
}
}
const dog: Dog = {
name: "elice",
tail: {
살랑살랑() {
console.log("꼬리 살랑살랑~");
},
},
주인: {
call(dogName: string) {
console.log("elice~");
},
},
};
// 위 if문을 optional chaining형식으로 변환
type Tail = {
살랑살랑: () => void;
};
type Human = {
call: (dogName: string) => void;
};
type Dog = {
name: string;
tail?: Tail;
주인?: Human;
};
function petDog(dog: Dog) {
dog.주인?.call(dog.name); 💙
dog.tail?.살랑살랑(); 💛
}
const dog: Dog = {
name: "elice",
tail: {
살랑살랑() {
console.log("꼬리 살랑살랑~");
},
},
주인: {
call(dogName: string) {
console.log("elice~");
},
},
};
petDog(dog)
// elice~ 💙
// 꼬리 살랑살랑~ 💛
A ?? B
||
와 ??
비교function getPrice(product: {
price?: number
}) {
return product.price || -1;
}
}
product.price가 false, 0, ‘’ 등(falsy값)일 때 -1
→ price가 0인 경우 -1을 반환해버림
function getPrice(product: {
price?: number
}) {
return product.price ?? -1;
}
}
product.price가 null, undefined일 때만 -1
→ price가 0인 경우 0 반환
예시
class User{
constructor(private id: string) {}
setId(id: string): void;
setId(id: number): void;
setId(id: string| number): void{
this.id = typeof id === 'number' ? id.toString() : id;
}
}
string| number
: id가 string 혹은 number가 올 수 있기 때문에 union type으로 지정typeof id === 'number' ? id.toString() : id;
: id의 타입이 number이면 toString()
적용. 그렇지 않으면 id 그대로 출력// tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
yarn add reflect-metadata
예를 들어, 데코레이터 @sealed
를 사용하면 다음과 같이 sealed
함수를 작성할 수 있음
function sealed(target) {
// 'target' 변수와 함께 무언가를 수행합니다.
}
고차함수 → 함수를 반환하는 함수
팩토리 → 어떤 인스턴스를 생성하여 반환하는 함수(메서드)
데코레이터 팩토리 → 데코레이터 함수를 반환하는 함수
⇒ 데코레이터 팩토리는 고차함수의 일종으로 데코레이터 함수를 반환하는 함수이다.
⇒ 데코레이터 함수를 커스터마이징할 때 사용
function color(value: string) { // 데코레이터 팩토리
return function (target) { // 데코레이터
// 'target'과 'value' 변수를 가지고 무언가를 수행합니다.
};
}
function decoratorFacotry(value: string) {
return function( // 함수를 반환 (이때 이 함수는 데코레이터 함수이다)
target: any,
propertyKey: string,
propertyDescriptor: PropertyDescriptor
) {
console.log(value)
}
}
class ExampleClass {
@decoratorFacotry("룰루랄라 퇴근이다")
static method() {}
}
ExampleClass.method(); // 룰루랄라 퇴근이다
데코레이터 함수는 특정 인자만 받는다.
데코레이터에 추가적인 인자를 주고 싶다면 고차함수에 매개변수를 추가하여 데코레이터 함수 내부에서 사용할 수 있다.
function first() {
console.log("first(): factory evaluated");
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("first(): called");
};
}
function second() {
console.log("second(): factory evaluated");
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("second(): called");
};
}
class ExampleClass {
@first()
@second()
method() {
console.log('method is called')
}
}
<실행결과>
first(): factory evaluated
second(): factory evaluated
second(): called
first(): called
method is called
→ 위의 실행결과로 알 수 있듯이, 단순히 console.log으로 출력하면 위에서 아래로 실행되고,
함수의 형태로 return되는 순서는 아래에서 위로 실행됨
→ method 메서드는 맨 마지막에 출력
예시1
// 값을 반환하지 않는 데코레이터 팩토리 함수
function sealed(constructor: Function)
{
Object.seal(constructor)
Object.seal(constructor.prototype)
}
@sealed
class Post {
constructor(public title: string) {} // 그대로 사용
publish(createdAt: Date) {
console.log(`${this.title} is published at ${createdAt.toISOString()}`)
}
}
값을 반환하지 않는 경우 클래스의 기존 생성자 함수를 그대로 사용
※ Object.seal
: 객체가 가진 기존 property를 수정, 추가, 삭제 등의 변경은 가하지 못한다.
예시2 - BugReport클래스에 데코레이터를 이용해 reportingURL 속성을 새로 추가
function reportableClassDecorator<T extends{ new(...args: any[]): {} }>constructor: T) {
return class extends constructor { // 💚
reportingURL = "http://www.example.com" // 💛
}
}
@reportableClassDecorator
class BugReport {
type = "report"
title: string
constructor(t: string) {
this.title = t
}
}
const bug = new BugReport("Needs dark mode")
console.log(bug)
// {type: 'report', title: 'Needs dark mode', reportingURL: 'http://example.com'}
1️⃣new(...args: any[]): {}
: new 키워드와 함께 어떠한 형식의 인수들도 받을 수 있는 타입<T extends{ 1️⃣ }>constructor: T)
: 1️⃣을 상속받는 제네릭타입(T)을 가지는 생성자(constructor)를 인수로 전달예시
functon deco(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log('데커레이터가 평가됨')
}
class TestClass {
@deco
test() {
console.log('함수 호출')
}
}
const t = new TestClass()
t.test()
// 데커레이터가 평가됨
// 함수 호출
만약 데커레이터에 인수를 넘겨서 데커레이터의 동작을 변경하고 싶다면, 데커레이터를 리턴하는 함수(데커레이터 팩터리)를 만들면 된다. ↓
functon deco(value: string) {
console.log('데커레이터가 평가됨')
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(value)
}
class TestClass {
@deco('HELLO')
test() {
console.log('함수 호출')
}
}
const t = new TestClass()
t.test()
// 데커레이터가 평가됨
// HELLO
// 함수 호출
나머지 데코레이터들은
📎 https://www.typescriptlang.org/ko/docs/handbook/decorators.html 참고
참고
※ 참고 : (도서) Nestjs으로 배우는 백엔드 프로그래밍