TypeScript
Part.2
chapter.5
5.1 함수 매개변수
- 함수의 매개변수 또한 타입을 선언할 수 있다.
- 타입을 선언하지 않은 매개변수는 any타입으로 간주되어 무엇이든 될 수 있다.
1 2 3 4 5 6 7
function sing(song) { console.log(`Singing: ${song}`); } // 선언되지 않은 매개변수 function sing(song: string) { console.log(`Singing: ${song}`); }
5.1.1 필수 매개변수
- 타입스크립트는 함수에 선언된 모든 매개변수가 필수라고 가정한다.
함수가 잘못된 수의 인수로 호출되면,타입스크립트는 타입 오류의 형태로 이의를 제기합니다.
1 2 3 4 5 6 7 8 9 10
function singTwo(first: string, second: string) { console.log(`${first}/${second}`); } singTwo("Ball"); // Error: Expected 2 arguments, but got 1. singTwo("Ball", "play"); // OK singTwo("Ball", "play", "friedns"); // Error: Expected 2 arguments, but got 3.
5.1.2 선택적 매개변수
- 함수 매개변수를 의도적으로 제공하지 않아 의도적으로 undefined를 사용할 수도 있다.
- 선택적 객체 타입 속성과 유사하게 타입 애너테이션의 : 앞에 ?를 추가해 매개변수가 선택적이라고 표시한다.
- 함수 호출에 선택적 매개변수를 제공할 필요는 없다.
선택적 매개변수에는 항상 undefined가 유니언 타입으로 추가되어 있다. 1 2 3 4 5 6 7 8 9 10 11
function Sing(song: string, singer?: string) { console.log(`Song ${song}`); if (singer) { console.log(`Singer ${singer}`); } } Sing("Any"); // OK Sing("Any", undefined); // OK Sing("Any", "Mary"); // OK
- 선택적 매개변수는 암묵적으로 undefined가 될 수 있다.
선택적 매개변수는 값이 undefined일지라도 항상 재공되어야 한다.
1 2 3 4 5 6 7
function Sing(song: string, singer: string | undefined) { console.log(`Song ${song} ${singer}`); } Sing("Any", undefined); // OK Sing("Any"); // Error: Expected 2 arguments, but got 1.
- 모든 선택적 매개변수는 마지막 매개변수여야 한다.
필수 매개변수 전에 선택적 매개변수가 먼저 온다면 오류가 발생한다.
1 2 3 4 5 6 7
function Sing(song: string, singer?: string){ ... } // OK function Song(song?: string, singer: string){ ... }// Error: A required parameter cannot follow an optional parameter.
5.1.3 기본 매개변수
- 선택적 매개변수에는 기본적으로 값이 제공되기 때문에 해당 타입스크립트 타입에는 암묵적으로 함수 내부에
| undefined
유니언 타입이 추가된다. - 함수의 매개변수에 대해 인수를 누락하거나 undefined 인수를 사용해서 호출하는 것을 허용한다.
=
는 암묵적으로undefined가 추가된다. 1 2 3 4 5 6 7 8 9 10
function Sing(song: string, list = 0){ ... } // list는 number|undefined Sing("Any"); // OK Sing("Any", 5); // OK Sing("Any", undefined); // OK Sing("Any", "1"); // Error: Argument of type "1" is not assignable to // parameter of type 'number:undefined'.
5.1.4 나머지 매개변수
- 자바스크립트의 일부 함수는 임의의 수의 인수로 호출할 수 있도록 만들어진다.
스프레드 연산자는 함수 선언의 마지막 매개변수에 위치하고, 해당 매개변수에서 시작해 함수에 전달된 나머지 인수가 모두 단일 배열에 저장되어야 함을 나타낸다.
1 2 3 4 5 6 7 8 9 10 11
function Sing(song: string, ...list: number[]) { console.log(song, list); } Sing("Any", 1, 2, 3, 4); // OK // "Any", [1,2,3,4] Sing("Any"); // OK // "Any", [] Sing("Any", "1"); // Error: Argument of type "string" is not assignable to parameter of // typer "number"
5.2 반환 타입
- 함수에 다른 값을 가진 여러 개의 반환문을 포함하고 있다면, 타입스크립트는 반환 타입을 가능한 모든 반환 타입의 조합으로 유추한다.
1 2 3 4
function Sing(song: string[], index: number) { return index < song.length ? song[index] : undefined; } // 타입: song: string[], index: number => string|undefined
5.2.1 명시적 반환 타입
- 변수와 마찬가지로 타입 애너테이션을 사용해 함수의 반환 타입을 명시적으로 선언하지 않는 것이 좋다.
- 함수에서 반환 타입을 명시적으로 선언하는 방식이 유용할 때도 종종 있다.
- 가능한 반환값이 많은 함수가 항상 동일한 타입의 값을 반환하도록 강제한다.
- 타입스크립트는 재귀 함수의 반환 타입을 통해 타입을 유추하는 것을 거부한다.
- 수백 개 이상의 타입스크립트 파일이 있는 매우 큰 프로젝트에서 타입스크립트 타입 검사 속도를 높일 수 있다.
함수 선언 반환 타입 애너테이션은 매개변수 목록이 끝나는
)
다음에 배치되며, 함수 선언의 경우{
앞에 배치된다.1 2 3 4 5 6
function Sing(songs: string[], count = 0): number { return songs.length ? Sing(songs.slice(1), count + 1) : count; } // 화살표 함수의 경우 const Sing = (songs: string[], count = 0): number => songs.length ? Sing(songs.slice(1), count + 1) : count;
함수의 반환 타입으로 할당할 수 없는 값을 반환하는 경우 타입스크립트는 할당 가능성 오류를 표시한다.
1 2 3 4 5 6 7 8 9 10 11 12 13
function SongDate(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 } }
5.3 함수 타입
- 함수를 값으로 전달할 수 있으며, 함수를 가지기 위한 매개변수 또는 변수의 타입을 선언하는 방법이 필요하다.
함수 타입 구문은 화살표 함수와 유사하지만 함수 본문 대신 타입이 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
let nothing: () => string; // 매개변수가 없고 string 타입을 반환한다. let input: (song: string[], count?: number) => number; // string[]매개변수와 count 선택적 매개변수 및 number 값을 반환하는 함수임을 설명한다. const songs = ["Juice", "Shake if Off", "What's up"]; function runSing(getSongA: (index: number) => string { for(let i = 0; i < songs.length; i++){ console.log(getSongA(i)); } }) function getSong(index: number){ return `${songs[index]}`; } runSing(getSongA); // OK function logSong(song: string){ return `${song}`; } runSing(logSong); // Error: Argument of type "(song: string) => string" in 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".
- 두 함수를 서로 할당할 수 없다는 오류를 출력할 때 타입스크립트는 일반적으로 세 가지 상세한 단계를 제공한다.
runSing
에 대한 오류 메시지는 다음과 같은 내용을 담고 있다.- 첫 번째 들여쓰기 단계는 두 함수 타입을 출력한다.
- 다음 들여쓰기 단계는 일치하지 않는 부분을 지정한다.
- 마지막 들여쓰기 단계는 일치하지 않는 부분에 대한 정확한 할당 가능성 오류를 출력한다.
- 단계별로 제공하는 내용은 다음과 같다.
- logSong: (song: string) => string은 getSongA: (index: number) => string에 할당되도록 제공된 타입이다.
- logSong의 song 매개변수는 getSongA의 index 매개변수로 할당됩니다.
- song의 string 타입은 index의 number타입에 할당할 수 없다.
5.3.1 함수 타입 괄호
- 함수 타입은 다른 타입이 사용되는 모든 곳에 배치할 수 있다.
- 유니언 타입도 포함된다.
- 유니언 타입의 애너테이션에서 함수 반환 위치를 나타내거나 유니언 타입을 감싸는 부분을 표시할 때 괄호를 사용한다.
1 2 3 4
let returnString: () => string | undefined; // 타입은 stirng | undefined 유니언을 반환하는 함수이다. let maybeReturnString: () => string | undefined; // 타입은 undefined나 string을 반환하는 함수이다.
5.3.2 매개변수 타입 추론
타입스크립트는 선언된 타입의 위치에 제공된 함수의 매개변수 타입을 유추할 수 있다.
1 2 3 4 5 6
let singer: (song: string) => string; singer = function (song) { return `Singing: ${song.toUpperCase()}`; // OK // song: string 타입 };
함수를 매개변수로 갖는 함수에 인수로 전달된 함수는 해당 매개변수 타입도 유추할 수 있다.
1 2 3 4 5
const songs = ["Call me", "Jolene", "The Chain"]; songs.forEach((song, index) => { console.log(`${song} is at index ${index}`); });
5.3.3 함수 타입 별칭
- 함수 타입에서도 타입 별칭을 사용할 수 있다.
string 타입을 매개변수로 받고, number 타입으로 반환하는 함수의 예시
1 2 3 4 5 6 7 8
type StringToNumber = (input: string) => number; let stringToNumber: StringToNumber; stringToNumber = (input) => input.length; // OK stringToNumber = (input) => input.toUpperCase(); // Error: Type 'string' is not assignable to type 'number'.
1 2 3 4 5 6 7 8 9 10
type NumberToString = (input: number) => string; function usesNumberToString(numberToString: NumberToString) { console.log(`The string is: ${numberToString(1234)}`); } userNumberToString((input) => `${input}! Hooray!`); // OK userNumberToString((input) => input * 2); // Error: Type 'number' is not assignable to type 'string'.
5.4 그 외 반환 타입
5.4.1 void 반환 타입
- return 문이 없는 함수이거나 값을 반환하지 않는 return 문을 가진 함수는 어떤 값도 반환하지 않는다.
타입스크립트는 void 키워드를 사용해 반환 값이 없는 함수의 반환 타입을 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10
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를 사용하면 함수에서 반환되는 모든 값은 무시된다.
1 2 3 4 5 6
let songLogger: (song: string) => void; songLogger = (song) => { console.log(`${songs}`); }; songLogger("Heart of Glass"); // OK
- 자바스크립트 함수는 실젯값이 반환되지 않으면 기본으로 모두 undefined를 반환하지만 void 는 undefined 와 동일하지 않다.
void는 함수의 반환 타입이 무시된다는 것을 의미하고 undefined 는 반환되는 리터럴 값이다.
1 2 3 4 5 6 7 8
function returnsVoid() { return; } let lazyValue: string | undefined; lazyValue = returnsVoid(); // Error: Type 'void' is not assignable to type 'string|undefined'.
- undefined 와 void 를 구분해서 사용하면 매우 유용하다.
- void 를 반환하도록 선언된 타입 위치에 전달된 함수가 반환된 모든 값을 무시하도록 설정할 때 유용하다.
- forEach 에 제공되는 함수는 원하는 모든 값을 반환할 수 있다.
void 타입은 자바스크립트가 아닌 함수의 반환 타입을 선언하는 데 사용하는 타입스크립트 키워드이며, 함수의 반환값이 자체적으로 반환될 수 있는 값도 아니고, 사용하기 위한 것도 아니다.
1 2 3 4 5 6 7
const records: string[] = []; function saveRecords(newRecords: string[]) { newRecords.forEach((record) => records.push(record)); } saveRecords(["21", "Come on Over", "The Bodyguard"]);
5.4.2 never 반환 타입
- 일부 함수는 값을 반환하지 않으며, never 반환 함수는 의도적으로 항상 오류를 발생시키거나 무한 루프를 실행하는 함수이다.
함수가 절대 반환하지 않도록 의도하려면 명시적 : never 타입 애너테이션을 추가해 해당 함수를 호출한 후 모든 코드가 실행되지 않음을 나타낸다.
1 2 3 4 5 6 7 8 9 10 11
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 ${typeof param}`); } param.toUpperCase(); // OK }
5.5 함수 오버로드
- 선택적 매개변수와 나머지 매개변수만으로 표현할 수 없는 매우 다른 매개변수들로 호출될 수 있으며, 이러한 함수를 오버로드 시그니처라고 부르는다.
- 하나의 최종 구현 시그니처와 그 함수의 본문 앞에 서로 다른 버전의 함수 이름, 매개변수, 반환 타입을 여러 번 선언한다.
함수 오버로드는 복잡하고 설명하기 어려운 함수 타입에 사용하는 최후의 수단이기 때문에 가능하면 함수 오버로드를 사용하지 않는 것이 좋다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
function createDate(timestamp: number): Date; function createDate(month: number, day: number, year: nmumber): Date; function createDate(monthOrTimestamp: number, day?: number, year?: nmumber) { return day === undefined || year === undefined ? new Date(monthOrTimestamp) : new Date(year, monthOrTimestamp, day); } createDate(554356800); // OK createDate(7, 27, 1987); // OK createDate(4, 1); // Error: No overload expects 2 arguments, but overloads // do exist that expect either 1 or 3 arguments.
- createDate 함수는 1개의 timestamp 매개변수 또는 3개의 매개변수 (month, day, year)를 사용해 호출한다.
- 허용된 수의 인수를 사용해 호출할 수 있지만 2개의 인수를 사용해 호출하면 2개의 인수를 허용하는 오버로드 시그니처가 없기 떄문에 타입 오류가 발생한다.
5.5.1 호출 시그니처 호환성
- 오버로드된 함수의 구현에서 사용되는 구현 시그니처는 매개변수 타입과 반환 타입에 사용하는 것과 동일하다.
함수의 오버로드 시그니처에 있는 함수 타입과 각 매개변수는 구현 시그니처에 있는 동일한 인덱스의 매개변수에 할당할 수 있어야 한다.
1 2 3 4 5 6 7 8 9
function format(data: string): string; // OK function format(data: string, neadle: string, haystack: string): string; // OK function format(getData: () => string): string; // Error: This overload signature is not compatible with its implementation signature. function format(data: string, needle?: string, haystack?: string) { return needle && haystack ? data.replace(needle, haystack) : data; }
- format 함수의 시그니처는 첫 번째 매개변수를 string으로 선언한다.
- 두 개의 오버로드 시그니처는 string 타입과 호환되지만, 세 번째 오버로드 시그니처의 () => string 타입과는 호환되지 않는다.
5.6 마치며
- 타입 애너테이션으로 함수 매개변수 타입을 선언할 수 있다.
- 타입 시스템의 동작을 변경하기 위한 선택적 매개변수, 기본 매개변수, 나머지 매개변수를 선언한다.
- 타입 애너테이션으로 함수 반환 타입을 선언할 수 있다.
- void 타입으로 사용 가능한 값을 반환하지 않는 함수를 알아낼 수 있다.
- never 타입으로 절대 반환하지 않는 함수를 알아낼 수 있다.
- 함수 오버로드를 사용해서 다양한 함수 호출 시그니처를 실행할 수 있다.