프론트엔드 개발/Typescript

타입스크립트 함수

하이고니 2023. 3. 2. 19:17

선택적 매개변수란?

 

자바스크립트에서 함수에 매개변수가 제공되지 않으면 함수 내부의 인수값은 undefined으로 설정된다. 함수에 매개변수를 제공할 필요가 없을 수도 있고, undefined 값을 의도적으로 사용할 수도 있다. 이런 경우에는 타입 애너테이션의 : 앞에 ?를 추가해 매개변수가 선택적이라고 표시할 수 있다.

 

선택적 매개변수에는 항상 | undefined가 유니언 타입으로 추가되어 있다.

 

 

아래 코드에서 매개변수 singer는 선택 사항으로 표시된다. 타입은 string | undefined

 

function announceSong(song: string, singer?: string) {
	console.log(`Song: ${song}`);
    
    if (singer) {
    	console.log(`Singer: ${singer}`);
    }
}

announceSong("Greensleeves");	// Ok
announceSong("Greensleeves", undefined);	// Ok
announceSong("Greensleeves", "Sia");	// Ok

 

 

선택적 매개변수는 | undefined를 포함하는 유니언 타입 매개변수와는 다르다. 값이 명시적으로 undefined인 매개변수는 어찌 됐든 항상 '입력'되어야 한다.

 

function announceSongBy(song: string, singer: string | undefined) {/* ... */}
announceSongBy("Sprinkler");
//~~~~~~~~~~~~~~~~~~~~~~~~~
// Error: Expected 2 arguments, but got 1.

 

매개변수가 여러개라면 선택적 매개변수는 마지막에 위치해야 한다.

 

 

반환 타입


타입스크립트는 지각적이다. 함수가 반환할 수 있는 모든 값을 이해하면 함수가 반환하는 타입을 알 수 있다. 타입스크립트는 함수 singSongsnumber를 반환한다고 생각한다.

 

// 타입: (songs: string[]) => number
function singSongs(songs: string[]) {
	for (const song of songs) {
    	console.log(`${song}`);
    }
    return songs.length;
}

 

함수의 반환 타입이 여러 개라면, 타입스크립트는 반환 타입을 '가능한 모든 반환 타입의 조합'으로 유추한다.

다음 코드에서 getSongAt 함수는 string | undefined를 반환하는 것으로 유추된다.

 

// 타입: (songs: string[], index: number) => stirng | undefined
function getSongAt(songs: string[], index: number) {
	return index < songs.length
    	? songs[index]
        : undefined;
}

 

 

명시적 반환 타입

 

함수에서 반환타입을 명시적으로 선언하는 것은 종종 유용하게 쓰인다.

 

  • 반환값이 여러 개인 함수가 항상 동일한 타입의 값을 반환하도록 강제할 수 있다.
  • 타입스크립트는 재귀 함수의 반환 타입을 통해 타입을 유추하는 것을 거부한다.
  • 매우 큰 프로젝트에서 타입 검사 속도를 높일 수 있다. 
function singSongsRecursive(songs: string[], count = 0): number {
	return songs.length ? singSongsRecursive(songs.slice(1), count + 1) : count;
}

 

 

화살표 함수의 경우 => 앞에 타입을 쓴다.

 

const singSongRecursive = (songs: string[], count = 0): number =>
	songs.length ? singSongsRecursive(songs.slice(1), count + 1) : count;

 

 

함수의 반환문이 함수의 반환 타입으로 할당할 수 없는 값을 반환하는 경우 타입스크립트는 할당 가능성 오류를 표시한다.

 

다음 getSongRecordingDate 함수는 Date | undefinded를 반환하도록 선언되었지만, 반환문 중 하나가 string을 반환하도록 되어 있다.

 

function getSongRecordingDate(song: string):
Date | undefined {
	switch (song) {
    	case "Strange Fruit":
            return new Date('April 20, 1939');	// Ok
        
        case "Greensleeves":
            return "unknown";
            //~~~~~~~~~~~~~~~
            // Error: Type 'string' is not assignable to type 'Date'.
        default:
            return undefined;	// Ok
    }
}

 

 

함수 타입


자바스크립트에서는 함수를 값으로 전달할 수 있다. 즉, 함수를 가지기 위한 매개변수 또는 변수의 타입을 선언하는 방법이 필요하다.

함수 타입 구문은 화살표 함수와 유사하지만 함수 바디 대신 타입을 가지고 있다.

 

const nothingInGivesString: () => string;
const inputAndOutput: (songs: string[], count?: number) => number;

 

함수 타입은 콜백 매개변수(함수로 호출되는 매개변수)를 설명하는 데 자주 사용된다.

 

예를 들어, 다음 runOnSongs 함수는 getSongAt 매개변수 타입을

index: number를 받고 string을 반환하는 함수' 로 선언했다. getSongAt을 전달하면 해당 타입과 일치하지만, logSong은 매개변수로 number 대신 string을 사용하므로 반환값을 가져오는 데 실패한다.

 

const songs = ["Juice", "Shake It Off", "What's Up"];

function runOnSongs(getSongAt: (index: number) => string) {
  for (let i = 0; i < songs.length; i += 1) {
    console.log(getSongAt(i));
  }
}

function getSongAt(index: number) {
  return `${songs[index]}`;
}
runOnSongs(getSongAt);  // Ok

function logSong(song: string) {
  return `${song}`;
}

runOnSongs(logSong);
//         ~~~~~~~
// Error: Argument of type '(song: string) => string' is
// not assignable to parameter of type '(index: number) => string'.
// Types of parameters 'song' and 'index' are incompatible.
// Type 'number' is not assignable to type 'string'.

 

함수 타입 괄호

 

함수 타입은 다른 타입이 사용되는 모든 곳에 배치할 수 있다. 여기에는 유니언 타입도 포함된다.

유니언 타입의 애너테이션에서 함수 반환 위치를 나타내거나 유니언 타입을 감싸는 부분을 표시할 때 괄호를 사용한다.

 

// string | undefined 유니언 타입을 반환하는 함수
let returnStringOrUndefined: () => string | undefined;

// 자신이 타입 자체가 undefined이거나, string을 반환하는 함수 
let mabyeReturnsString: (() => string) | undefined;

 

매개변수 타입 추론

 

매개변수로 사용되는 인라인 함수를 포함하여, 작성된 모든 함수에 대해 매개변수를 선언해야 한다면 굉장히 번거로울 것이다. 다행히 타입스크립트는 선언된 타입의 위치에, 제공된 함수의 매개변수 타입을 유추할 수 있다.

 

let singer: (song: string) => string;	// string 타입의 매개변수를 갖는 함수

singer = function (song) {	// 여기서 매개변수의 타입을 선언하지 않아도
    return `Singing: ${song.toUpperCase()}!`;	// Ok
}

 

함수를 매개변수로 갖는 함수에 인자로 전달된 함수는 해당 매개변수 타입도 잘 유추할 수 있다.

 

const songs = ["Call Me", "Jolene", "The Chain"];

// song: string
// index: number
songs.forEach((song, index) => {	// song: string, index: nubmer로 유추
	console.log(`${song} is at index ${index}`);
})

 

 

그 외 반환 타입


void 반환 타입

 

일부 함수는 어떤 값도 반환하지 않는다.

 

예를 들면 return 문이 없는 함수, 값을 반환하지 않는 return 문을 가진 함수의 경우가 있다.

타입스크립트는 void 키워드를 사용해 반환값이 없는 함수의 반환 타입을 확인할 수 있다.

 

function logSong(song: string | undefined): void {
    if (!song) {
    	return;	// Ok
    }
    console.log(`${song}`);
    return true;
    //~~~~~~~~~
    // Error: Type 'boolean' is not assignable to type 'void'.
}

 

함수 타입 선언 시 void 반환 타입은 매우 유용하다. 함수 타입을 선언할 때 void를 사용하면 함수에서 반환되는 모든 값은 무시된다.

 

let songLogger: (song: string) => void;

songLogger = (song) => {
    console.log(`${song}`);
};

songLogger("Heart of Glass");	// Ok

 

자바스크립트의 함수는 값이 반환되지 않으면 기본적으로 모두 undefined를 반환하지만 void는 undefined와 동일하지 않다.

void는 함수의 반환 타입이 무시된다는 것을 의미하고 undefined는 반환되는 리터럴 값이다. undefined를 포함하는 대신 void 타입의 값을 할당하려고 하면 타입 오류가 발생한다.

 

function returnsVoid() {
    return;
}

let lazyValue: string | undefined;
lazyValue = retrunsVoid();
//~~~~~~~
// Error: Type 'void' is not assignable to type 'string | undefined'.

 

undefined와 void를 구분해서 사용하면 매우 유용하다.

특히 void를 반환하도록 선언된 타입 위치에 전달된 함수가 반환된 모든 값을 무시하도록 설정할 때 유용하다.

 

예를 들어 배열의 내장 forEach 메서드는 void를 반환하는 콜백을 받는다. forEach에 제공되는 함수는 원하는 모든 값을 반환할 수 있다. 다음 saveRecords 함수의 records.push(record)number(배열의 .push()에서 반환된 값)를 반환하지만, 여전히 newRecords.forEach에 전달된 화살표 함수에 대한 반환값이 허용된다.

 

const records: string[] = [];

function saveRecords(newRecords: string[]) {
    newRecords.forEach(record => records.push(record));
}

saveRecords(['21', 'Come On Over', 'The Bodyguard'])

 

void 타입은 자바스크립트가 아닌 함수의 반환 타입을 선언하는 데 사용하는 타입스크립트 키워드다. void 타입은 함수의 반환값이 자체적으로 반환될 수 있는 값도 아니고, 사용하기 위한 것도 아니라는 표시다.

 

 

void의 개념이 헷갈려서 두 가지 함수를 만들어봤다.

 

 

test1과 2는 똑같이 number 타입 두 개를 받아서 void 타입을 반환하는 형태의 함수다. 하지만 위의 것은 리턴할 때 오류가 발생하고 아래 것은 오류가 발생하지 않는다. 차이가 궁금해서 chatGPT에게 물어봤다. 대답은 아래와 같았다.

 

 

더 모르겠다.. 똑같이 반환하려는 시도가 있는 것이거나 똑같이 반환하려는 시도가 없는 것이라고 봐야할 것 같은데, 왜 두 개를 다르게 유추할까?

 

 

Subsitutability

 

Contextual 함수 타이핑(식의 한 변에만 타입을 지정하는 것)으로 void 반환을 지정해주는 것은,

함수에게 '아무 것도 반환하지 않을 것'을 강제하지 않는다.

 

 

... 라는 것이 있는 것 같은데 잘 이해가 안 돼서 해당 블로그에 댓글을 남겨놓은 상태다.

 

 

[TypeScript] void vs undefined

아래의 코드는 우아한테크코스 지원 플랫폼의 타입스크립트 전환 작업을 진행하면서 마주쳤던 이슈입니다. axios interceptors가 성공 응답을 처리할 때 body가 없는 경우 리턴 타입을 다음 둘 중에서

woojeongmin.com

 

 

 

+답변을 바탕으로 정리한 내용

 

위에 작성한 코드를 보기 쉽게 다시 쓰면 아래와 같다.

 

// void를 반환하는 함수 본문에서 number 타입 반환
const test1 = (num1: number, num2: number): void => {
  // @ts-expect-error
  return num1 + num2; // error 발생
}

// 좌변의 test2에 타입을 지정, void를 반환하지 않는 함수(sum)를 할당
const test2 = (num3: number, num4: number): number => {
  return num3 + num4;
};

type Func = (num3: number, num4: number) => void;
const test2: Func = test2;

 

먼저 test1은 void를 반환하는 함수 본문에서 number 타입을 반환하려는 상황이다.

함수에 명시적으로 void를 반환하겠다고 지정했기 때문에 함수 본문에서 void에 할당 가능하지 않은 타입(number) 반환 시 에러를 발생시킨다.

test2는 좌변의 변수(Func)를 void를 반환하는 함수 타입으로 지정한 후, number 타입을 반환하는 함수를 할당하는 상황이다.

void를 반환하는 함수에 다른 타입을 반환하는 함수를 할당 가능한 것은 Sustitutability 때문이다.

 

후자가 가능한 이유는,

반환값이 필요없는 함수에 '어떤 함수든 받아 반환값만 사용하지 않고 쉽게 사용할 수 있도록' 타입스크립트가 설계되었기 때문이다.

 

예를 들어 Array의 forEach 함수는 void를 반환하는 함수를 받는다. 콜백 함수의 반환값을 사용하지 않는다는 것을 나타내기 위함이다. 하지만 test2와 같이 number를 반환하는 함수도 할당 가능하다. 무엇을 반환하든 동작은 하기 때문에