TIL. typescript basic

Teasan·2021년 7월 1일
0

typescript

목록 보기
4/13
post-thumbnail

이 노트는 드림코딩의 [타입스크립트 + 객체지향 프로그래밍 마스터] 강의를 base로 작성되었습니다.

함수에서 타입 정리하기


JS

function jsAdd(num1, num2) {
    return num1 + num2;
  }

jsAdd라는 함수는 num1과 num2라는 인자 두개를 받아서 더해주는 간단한 함수이다. 이 함수는 간단하고 코드도 짧기 때문에, 어떤 기능을 수행하고 있는지 파악하기가 쉽지만, 함수가 길고 복잡한 경우에는 어떤 기능을 수행하고 있는지 파악하기가 상대적으로 어려울 것이라 생각된다. 그리고 num1과 num2라는 인자는 숫자를 받아오는 인자일 것이라고 추측할 수 있으나, 만약 실수로 문자열을 전달하게 되면 내가 해당 함수에서 원했던 기능을 제대로 수행하지 못할 가능성이 높아진다.

이때 함수의 기능을 조금 더 명확하게 수행할 수 있도록 도와주는 것이 바로 함수에서의 타입 정의이다.

TS

function add(num1: number, num2: number): number {
    return num1 + num2;
  }
  add(1, 2);

이번에는 동일한 기능을 수행하는 함수의 인자에 타입을 더했다. 앞서 명시한대로 num1과 num2는 숫자 타입(number)로 각각 지정하였고, 누가 보아도 이 함수에는 숫자 타입의 인자를 전달해야 하고, 리턴하는 값도 숫자 타입일 것이라고 정확하게 이해할 수 있을 것이다.

이번에는 조금 더 복잡한 함수를 예시로 들어보겠다.

JS

function jsFetchNum(id) {
    //code ..
    //code ..
    return new Promise((resolve, reject) => {
      resolve(100);
    });
  }

jsFetchNum이란 함수는 id라는 인자를 받아서 무언가를 fetch하여 코드를 발생시키고, promise를 리턴하는 함수라 추측 가능하다. 하지만 중간에 생략되어 있는 코드들이 끝도 없이 늘어져 있다고 상상해보면, 해당 함수가 어떤 것을 리턴하고 있는지 곧바로 확인하기가 어려울 가능성이 높을 것이다. (적어도 나는 그렇다.) 이번에도 해당 함수에 타입을 더해서 조금 더 명확하게 이해할 수 있도록 해보자.

TS

function tsFetchNum(id: string): Promise<number> {
    // Promise 중에서도 숫자를 리턴하겠다는 뜻
    //code .. 중략
    //code .. 중략
    return new Promise((resolve, reject) => {
      resolve(100);
    });
  }

앞서 작성된 로직에 타입을 더한 tsFetchNum 함수는 인자 id를 받는다. id는 내부 구현 사항이 없기 때문에, 아무거나 지정해줘도 가능하기에 일단 보편적으로 id의 타입으로 많이 사용하는 string으로 지정해주었다. 그리고 이 함수가 리턴하는 값은 Promise 이며, 숫자 타입을 Promise 하고 있기 때문에 Promise에 <number>로 타입을 지정해주었다.

이렇게 타입이 더해진 함수는 string 타입의 인자를 받아서 숫자를 fetch하고 완료된 이후에는 Promise가 숫자 타입의 데이터를 리턴한다는 것을 추측할 수 있다.

이처럼, static한 타입을 정의한다는 의미는 우리가 조금 더 안정적으로 프로그래밍을 할 수 있도록 도와준다는 뜻이며, 또한 이렇게 타입을 지정함으로써 조금 더 나은 방식으로 문서화를 구현해내는 효과 또한 얻을 수 있다는 뜻일 것이다.

함수 타입 이용하기(spread, default, optional)


앞에서는 자바스크립트 코드에 타입이 더해진 타입스크립트 코드를 간단히 비교해보았다.

자바스크립트는 최신 문법 뿐만 아니라, 자바스크립트에는 아직 포함되어져 있지 않은 타입스크립트에서만 제공하는 Syntax 문법을 사용할 수 있다. 이처럼 자바스크립트에서도 사용 가능하면서도 타입스크립트에 활용도를 높일 수 있는 타입이 더해진 함수 정의에 대해서 학습해보자.

1. Optional parameter

(before)

function printName(firstName: string, lastName: string) {
    console.log(firstName);
    console.log(lastName);
  }

인자에 타입이 더해진 printName 함수이다. 이 함수를 보면 string 타입의 인자를 각각 하나씩, 총 두개를 받아서 string 타입으로 출력하는 함수라는 것을 추측할 수 있다.

printName('Steve', "dam"); 
// Steve
// dam
// 정상 출력 된다.
printName('Ellie'); 
// error
// 경고 메세지가 뜬다. (인자 갯수가 다르기 때문에)
printName('min', 0); 
// error
// 경고 메세지가 뜬다. (타입이 다르기 때문에)

printName 함수를 호출해보자. 첫번째는 인자에 지정한 string 타입과 인자 갯수를 지켜서 함수를 호출했고 정상출력 되었다. 두번째는 인자에 지정한 string 타입을 지켰으나, 인자의 갯수(2개)에 맞지 않아, 경고 메세지(에러가 발생)가 뜬다. 세번째 역시 첫번째 인자는 string 타입을 지켜서 호출했으나, 두번째 인자에서 지정한 string 타입을 지키지 않았으므로 경고 메세지(에러가 발생)가 뜬다.

이처럼 정해놓은 인자의 타입과 갯수를 지켜서 작성하지 않으면, error가 발생한다. 만약, 해당 함수를 한 가지의 인자로도 출력 가능한 함수로 이용하고 싶다면 어떨까? 즉 지정한 인자의 타입과 갯수를 지킨 경우와 지정한 인자의 타입은 지키되 한개의 갯수로만 출력하고 싶은 경우에 모두 다 활용할 수 있는 함수를 만들고 싶을 때가 있을 것이다. 그럴 때 사용하는 것이 바로 Optional parameter 이다. (after)

(after)

function printNames(firstName: string, lastName?: string) {
    console.log(firstName);
    console.log(lastName);
  }

의도한 바에 따르면, 해당 함수의 첫번째 인자는 꼭 string 타입으로 전달해야 하지만, 두번째 인자는 타입대로 전달하지 않아도 된다. 갯수 역시 마찬가지이다. 일단, 전달하지 않아도 되는 인자 뒤에 물음표?를 더하여 string 타입으로 지정해놓았다. 이렇게 명시를 해놓으면, 이 string 타입의 인자는 타입별로 전달받을 수도 있고, 아예 인자를 전달 받지 않을 수도 있다는 의미가 된다.

 printNames('Steve', "dam"); 
// Steve
// dam
// 정상 출력 된다
  printNames('min'); 
// min
// 정상 출력 된다.
  printNames('Anna', undefined); 
// Anna
// undefined
// 정상 출력 된다.

해당 함수를 출력해보면, 인자의 타입과 갯수를 전부 지킨 첫번째 출력 뿐만 아니라 갯수를 지키지 않은 두번째 출력도, 그리고 두번째 타입을 undefined로 지정한 세번째 출력도 모두 정상 출력이 된다.

이처럼, Optional parameter를 사용함으로써인자의 갯수가 달라도, 인자의 타입을 지키지 않아도 error가 발생하지 않는 걸 확인할 수 있다.

Optional parameter과 비슷하지만 다른 방식도 있다.

function printNames(firstName: string, lastName: string | undefined) {
    console.log(firstName);
    console.log(lastName);
  }
  printNames('Steve', "dam"); // 정상 출력 된다.
  printNames('min'); // 경고 메세지가 뜬다.
  printNames('Anna', undefined); // 정상 출력 된다.

만약 Optional parameter를 이용하지 않고, lastName: string | undefined 라고 명시적으로 정의를 해놓으면, 무조건 정확하게 undefined라고 정의를 해줘야 한다. 이런 방법도 고려 가능하나, Optional parameter에 비해서 가독성도 떨어지고 간편하지 않으므로 되도록이면, Optianal parameter를 사용하는게 좋다.

2. Default parameter

function printMessage(message : string) {
    console.log(message);
  }
  printMessage();

printMessage 함수는 string 타입의 메세지를 출력하는 함수이다. 당연하게도 해당 함수를 출력하면서 인자를 전달하지 않으면 error가 발생하게 될 것이다. 만약 사용자가 해당 함수를 호출하면서 아무런 인자도 전달하지 않고, 기본 message를 출력하고 싶다면 Default parameter를 사용하면 된다.

function printMessages(message : string = 'default message') {
    console.log(message);
  }
  printMessages();
// 아무런 인자를 전달하지 않았으나, default message인 'default message'가 출력된다.

앞의 예시처럼 해당 함수에 아무런 인자를 전달하지 않았으나, Default parameter를 사용함으로써 error가 발생하지 않고, 해당 Detault parameter 기본 값으로 지정해놓은 'default message' 값이 출력된다.

만약 인자의 갯수의 제한 없이 원하는 만큼 전달하면 알아서 자동으로 전부 더해주는 함수를 만들어야한다면 어떻게 해야할까? 이때 사용하는 것이 바로 Rest parameter이다.

3. Rest parameter

function addNumbers(...numbers: number[]): number {
    return numbers.reduce((a, b) => a+b);
  }

addNumbers라는 함수는 전달받은 인자를 다 더한 다음에 최종으로 더해진 숫자를 리턴하는 기능을 수행하게 될 것이다. 이때 Rest parameter를 이용하면 간편하게 배열 형태로 받아올 수 있다.

...numbers

전달되는 모든 인자들을 Rest parameter를 이용해서 ...numbers라는 배열로 받아올 것이고, 숫자 타입의 배열로 지정했다. (만약 다른 타입의 데이터를 전달한다면 에러가 발생할 것이다.) numbers 라는 배열을 reduce를 이용해서 간단하게 계산할 수 있을 것이다. a와 b라는 숫자를 받아서 a와 b를 계속 더해가면 된다.

이렇듯, 갯수에는 상관없이 통일한 타입의 데이터를 함수의 인자로 전달할 때 Rest parameter를 사용할 수가 있다.

console.log(addNumbers(1, 2)); 
// 3
console.log(addNumbers(1, 2, 3, 4)); 
// 10 
console.log(addNumbers(1, 2, 3, 4, 5)); 
// 15

출처 :
이 포스팅은 드림코딩의 [타입스크립트+객체지향 프로그래밍 마스터 과정] 강의를 기반으로 작성했습니다. https://academy.dream-coding.com/

profile
일단 공부가 '적성'에 맞는 개발자. 근성있습니다.

0개의 댓글