TypeScript - Learning TypeScript Chp 5. Function

이소라·2023년 3월 23일
0

TypeScript

목록 보기
19/28

5. Functions

5.1 Function Paramters

  • 매개변수의 명시적 타입 정보가 선언되지 않으면, TypeScript가 매개변수의 타입을 any로 간주합니다.
  • TypeScript는 type annotation을 통해 함수 매개변수 타입을 선언할 수 있습니다.
function sing(song) {
  // paramter 'song': any
  console.log(`Singing: ${song}!`);
}

function sing(song: string) {
  // paramter 'song': string
  console.log(`Singing: ${song}!`);
}

5.1.1 Required Parameters

  • TypeScript는 함수에 선언된 모든 매개변수가 필수라고 가정합니다.
    • 함수가 너무 적거나 많은 인수로 호출되면, TypeScript는 인수의 개수를 계산하여 타입 오류의 형태로 이의를 제기합니다.
function singTwo(first: string, second: string) {
  console.log(`${first} / ${second}`);
}

// Error: Expected 2 arguments, but got 1 
singTwo('Ball and Chain');
// Ok
singTwo('I will Survive', 'Higher Love');
// Error: Expected 2 arguments, but got 3
singTwo('Go Your Own Way', 'The Chain', 'Dream');

5.1.2 Optional Parameters

  • 선택적 매개변수는 매개변수의 type annotation : 앞에 ?를 추가해서 표시합니다.
  • 함수 호출에 선택적 매개변수(optional paramter)를 제공할 필요는 없습니다.
  • 선택적 매개변수에는 항상 | undefined가 union 타입으로 추가되어 있습니다.
    • 선택적 매개변수는 항상 암묵적으로 undefined가 될 수 있습니다.
function announceSong(song: string, singer?: string) {
  console.log(`Song: ${song}`);
  
  if (singer) {
    console.log(`Singer: ${singer}`);
  }
}
// Ok
announceSong('Greensleeves');
// Ok
announceSong('Greensleeves', undefined);
// Ok
announceSong('Chandelier', 'Sia');
  • 선택적 매개변수는 | undefined를 포함하는 union 타입 매개변수와 다릅니다.
    • 선택적 매개변수가 아닌 매개변수는 값이 undefined일지라도 항상 제공되어야 합니다.
function announceSongBy(song: string, singer: string | undefined) { /* ... */}

// Error: Expected 2 arguments, but got 1
announceSongBy('Greensleeves');
// Ok
announceSongBy('Greensleeves', undefined);
// Ok
announceSongBy('Chandelier', 'Sia');
  • 함수에서 사용되는 모든 선택적 매개변수는 마지막 매개변수여야 합니다.
    • 필수 매개변수 전에 선택적 매개변수를 위치시키면 TypeScript 구문 오류가 발생합니다.
// Error: A required parameter cannot follow an optional parameter
function announceSinger(singer?: string, song: string) {/* ... */}

5.1.3 Default Paramters

  • JavaScript에서 선택적 매개변수를 선언할 때 =와 값이 포함된 기본값을 제공할 수 있습니다.
  • 매개변수에 기본값이 있고 type annotation이 없는 경우, TypeScript는 기본값을 기반으로 매개변수 타입을 유추합니다.
function rateSong(song: string, rating = 0) {
  console.log(`${song} gets ${rating}/5 stars!`);
}
// Ok
rateSong('Photograph');
// Ok
rateSong('Set Fire to the Rain', 5);
// Ok
rateSong('Set Fire to the Rain', undefined);
// Error: Argument of type '100' is not assignable to paratmeter of type 'number | undefined' 
rateSong('At Last', '100');

5.1.4 Rest Paramters

  • JavaScript에서 ... spread 연산자는 함수 선언의 마지막 매개변수에 위치하고, 해당 매개변수에서 시작해 함수에 전달된 나머지(rest) 인수가 모두 단일 배열에 저장되어야 함을 나타냅니다.
  • TypeScript는 이러한 나머지 매개변수(rest parameter)을 일반 매개변수와 유사하게 선언할 수 있습니다.
    • 인수 배열을 나타내기 위해 끝에 [] 구문이 추가된다는 점만 다릅니다.
function singAllTheSongs(singer: string, ...songs: string[]) {
  for (const song of songs) {
    console.log(`${song}, by ${singer}`);
  }
}
// Ok
singAllTheSongs('Alicia Keys');
// Ok
singAllTheSongs('Lady Gaga', 'Bad Romance', 'Just Dance', 'Poker Face');
// Error: Argument of type 'number' is not assignable to parameter of type 'string'
singAllTheSongs('Ella Fitzgerald', 2000);



5.2 Return Types

  • 함수가 반환할 수 있는 모든 값을 이해하면 함수가 반환하는 타입을 알 수 있습니다.
// function singSongs(songs: string[]) => number
function singSongs(songs: string[]) {
  for (const song of songs) {
    console.log(`${song}`);
  }
  return songs.length;
}
  • 함수에 다른 값을 가진 여러 개의 반환문을 포함하고 있다면, TypeScript는 반환 타입(return type)을 가능한 모든 반환 타입의 조합으로 유추합니다.
// function getSongAt(songs: string[], index: number) => string | undefined
function getSongAt(songs: string[], index: number) {
  return index < songs.length ? songs[index] : undefined;
}

5.2.1 Explicit Return Types

  • 아래와 같은 경우, 함수에서 반환 타입을 명시적으로 선언하는 것이 유용합니다.

    1. 가능한 반환값이 많은 함수가 항상 동일한 타입의 값을 반환하도록 강제합니다.
    2. TypeScript는 재귀 함수의 반환 타입을 통해 타입을 유추하는 것을 거부합니다.
    3. 수백 개 이상의 TypeScript 파일이 있는 매우 큰 프로젝트에서 TypeScript 타입 검사 속도를 높일 수 있습니다.
  • 함수의 반환 타입 type annotation은 매개변수 목록이 끝나는 )와 {의 사이에 배치됩니다.

    • 화살표 함수의 경우, 함수 반환 타입 type annotation이 => 앞에 배치됩니다.
function singSongsRecursive(songs: string[], count = 0): number {
  return song.length 
    ? singSongsRecursive(songs.slice(1), count + 1)
  : count;
}

const singSongsRecursive = (songs: string[], count = 0): number => {
  return song.length 
    ? singSongsRecursive(songs.slice(1), count + 1)
  : count;
}
  • 함수의 반환문이 함수의 반환 타입으로 할당할 수 없는 값을 반환하는 경우, TypeScript는 할당 가능성 오류를 표시합니다.
function getSongRecordingDate(song: string): 
Date | undefined {
  switch(song) {
    case 'Strange Fruit':
      // Ok
      return new Date('April 20, 1939');
    case 'Greensleeves':
      // Error: Type 'string' is not assignable to type 'Date'
      return 'unknown';
  }
}



5.3 Function Types

  • JavaScript에서 함수는 값으로 전달할 수 있습니다.

    • 함수를 가지기 위한 매개변수 또는 변수의 타입을 선언하는 방법이 필요합니다.
  • 함수 타입(function type) 구문은 화살표 함수와 유사하지만 함수 본문 대신 타입이 있습니다.

let nothingInGivesString: () => string;

let inputAndOutput: (songs: string[], count?: number) => number;
  • 함수 타입은 콜백 매개변수를 설명하는데 자주 사용됩니다.

    • 매개변수에 할당한 함수의 타입이 명시한 함수 타입과 일치하지 않으면, 할당 가능성 오류를 표시합니다.
  • TypeScript는 두 함수를 서로 할당할 수 없다는 오류를 출력할 때 일반적으로 3가지 단계를 제공합니다.

    1. 첫 번째 들여쓰기 단계는 두 함수 타입을 출력합니다.
    2. 다음 들여쓰기 단계는 일치하지 않는 부분을 지정합니다.
    3. 마지막 들여쓰기 단계는 일치하지 않는 부분에 대한 정확한 할당 가능성 오류를 출력합니다.
const songs = ["Juice", "Shake It Off", "What's up"];

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

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

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

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

5.3.1 Function Type Parenthesis

  • 함수 타입은 다른 타입이 사용되는 모든 곳에 배치할 수 있습니다.
    • union type annotation에서 함수 타입을 포함할 때 함수 타입을 괄호로 묶습니다.
let maybeReturnsString: (() => string) | undefined;

5.3.2 Parameter Type Inferences

  • TypeScript는 선언된 타입의 위치에 제공된 함수의 매개변수 타입을 유추할 수 잇습니다.
let singer: (song: string) => string;

singer = function (song) {
  // parameter 'song': string
  return `Singing: ${song.toUpperCase()}`;
}
  • 함수를 매개변수로 갖는 함수에 인수로 전달된 함수는 해당 매개변수 타입도 잘 유추할 수 있습니다.
const songs = ['Call Me', 'Jolene', 'The Chain'];

songs.forEach((song, index) => {
  // parameter 'song': string
  // parameter 'index': number
  console.log(`${song} is at index ${index}`);
})

5.3.3 Function Type Alias

  • 함수 타입에서도 type alias를 사용할 수 있습니다.
type StringToNumber = (input: string) => number;

let stringToNumber: StringToNumber;

// Ok
stringToNumber = (input) =>  input.length;
// Error: Type '(input: string) => string' is not assignable to type 'StringToNumber'
	// Type 'number' is not assignable to 'string'
stringToNumber = (input) => input.toUpperCase();



5.4 Void, Never Return Types

5.4.1 Void Returns

  • TypeScript에서 void는 반환값이 없는 함수의 반환 타입에 사용됩니다.
    • 반환 타입이 void인 함수는 값 반환을 허용하지 않습니다.
function logSong(song: string | undefined): void {
  if (!song) {
    return;
  }
  
  console.log(`${song}`);
  // Error: Type 'boolean' is not assignable to type 'void'
  return true;
}
  • JavaScript에서 함수는 실제값을 반환하지 않으면 모두 undefined를 반환합니다.
  • TypeScript에서 void와 undefined는 같지 않습니다.
    • void는 함수의 반환 타입이 무시된다는 것을 의미합니다.
    • undefined는 초기화되지 않은 값을 의미합니다.
function returnsVoid() {
  return;
}

let lazyValue: string | undefined;
// Error: Type 'void' is not assignable to type 'string | undefined'
lazyValue = returnsVoid();
  • 함수의 반환 타입을 void로 선언하면, 함수에서 반환되는 모든 값은 무시됩니다.
    • 예: forEach 메서드의 콜백 함수는 void를 반환합니다.
      • 반환값이 무시되므로, 원하는 모든 값을 반환할 수 있습니다.
const records: string[] = [];

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

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

5.4.2 Never Returns

  • nver 반환 함수는 (의도적으로) 항상 오류를 발생시키거나 무한 루프를 실행하는 함수입니다.
    • 함수의 반환 타입이 never이면, 해당 함수를 호출한 후 모든 코드가 실행되지 않음을 나타냅니다.
function fail(message: string): never {
  throw new Error(`Invariant failure: ${message}`);
}

function workWithUnsafeParam(param: unknown) {
  if (typeof param !== 'string') {
    fail(`param should be a string, not ${param}`);
  }
  // parameter 'param': string
  param.toUpperCase();
}

Note

  • void : 아무것도 반환하지 않는 함수의 반환 타입
  • never : 절대 반환하지 않는 함수의 반환 타입



5.5 Function Overloads

  • 선택적 매개변수와 나머지 매개변수만으로 표현할 수 없는 매우 다른 매개변수들로 호출되는 함수를 오버로드 시크니처(overload signature)로 설명할 수 있습니다.
  • 오버로드 시그니처는 하나의 최종 구현 시그니처(implementation signature)와 그 함수의 본문 앞에 서로 다른 버전의 함수 이름, 매개변수, 반환 타입을 여러 번 선언합니다.
  • 오버로드된 함수 호출에 대해 구문 오류를 생성할지 여부를 결정할 때, TypeScript는 함수의 오버로드 시그니처만 확인합니다.
// overload signature
function createDate(timestamp: number): Date;
function createDate(month: number, day: number, year: number): Date;
// implementation signature
function createDate(monthOrTimestamp: number, day?: number, year?: number) {
  return day === undefined || year === undefined
    ? new Date(monthOrTimestamp)
    : new Date(year, monthOrTimestamp, day);
}

// Ok
createDate(554356800);
// Ok
createDate(7, 27, 1987);
// Error: No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
createDate(4, 1):
  • TypeScript를 컴파일해 JavaScript로 출력하면, 오버로드 시그니처도 지워집니다.
function createDate(monthOrTimestamp, day, year) {
  return day === undefined || year === undefined
    ? new Date(monthOrTimestamp)
    : new Date(year, monthOrTimestamp, day);
}

5.5.1 Call-Signature Compatibility

  • 함수의 오버로드 시그니처에 있는 반환 타입과 매개변수는 구현 시그니처에 있는 동일한 인덱스의 매개변수에 할당할 수 있어야 합니다.
    • 구현 시그니처는 모든 오버로드 시그니처와 호환되어야 합니다.
// Ok
function format(data: string): string;
// Ok
function format(data: string, needle: string, haystack: string): string;
// Error: This overload siganature is not competible with its implementation signature
function format(getData: () => string): string;
function format(data: string, needle?: string, haystack?: string) {
  return needle & haystack ? data.replace(needle, haystack) : data;
}



참고

  • Learning TypeScript Chap. 5 Functions

0개의 댓글