함수 (Function)
소개
함수는 JavaScript의 응용 프로그램을 구성하는 기본 요소입니다. 그리고 함수는 추상화 계층, 클래스, 정보 숨기기, 모듈을 모방하는 방법입니다. TypeScript에서는 클래스, 네임 스페이스 및 모듈이 있지만 함수는 여전히 작업 수행 방법을 설명하는 데 중요한 역할을합니다. 또한 TypeScript는 표준 JavaScript 함수에 몇 가지 새로운 기능을 추가하여 보다 쉽게 작업할 수 있도록합니다.
함수
JavaScript와 마찬가지로, 명명 함수(Named function) 또는 익명 함수(Anonymous function)로 TypeScript 함수를 생성할 수 있습니다. 이를 통해 애플리케이션에 가장 적합한 방법을 선택할 수 있으며, API에서 기능 목록을 작성하든, 다른 기능으로 이전할 수있는 일회성 기능을 사용하든 상관 없습니다.
JavaScript에서 이러한 두 가지 접근 방식은 아래의 예제를 보면 쉽게 이해할 수 있습니다.
|
|
JavaScript와 마찬가지로 TypeScript 함수는 함수 본문 외부의 변수를 참조할 수 있습니다. 이런 행위를 보통 변수를 Capture 한다고 합니다. 이것이 어떻게 작동하는지 이해하는 것과, 사용에 대한 trade-off는 이 문서의 범위를 벗어납니다. 하지만 이러한 메커니즘이 JavaScript와 TypeScript에서 아주 중요한 요소라는 것을 이해하고 있어야 합니다.
|
|
함수 타입
함수 작성하기
우선 이전의 간단한 예제에 타입을 추가해 보겠습니다.
|
|
각 파라미터에 타입을 추가 하고, 리턴 타입을 추가하여 함수에 타입을 부여할 수 있습니다. TypeScript는 return 문을 보고 리턴 타입을 파악할 수 있으므로 많은 경우에 선택적으로 리턴 타입을 생략할 수도 있습니다.
함수 타입 작성
함수를 작성 했으니 함수의 각 타입을 살펴보고 함수의 전체 타입을 작성해 보겠습니다.
|
|
위 코드의 함수 형식은 파라미터의 타입과 리턴 타입의 두 부분이 동일합니다. 전체 함수 타입을 작성할 때 두 부분이 필요합니다. 우리는 파라미터 목록과 마찬가지로 파라미터 타입을 작성하여 각 파라미터에 이름과 타입을 제공합니다. 이름은 가독성을 돕기위한 것입니다. 다음과 같이 작성할 수 있습니다.
|
|
파라미터 타입이 있으면 함수 타입에 파라미터를 지정하는 이름에 상관없이 함수의 유효한 타입으로 간주됩니다.
두 번째 부분은 리턴 타입입니다. 파라미터와 리턴 타입 사이에 굵은 화살표 (=>
)를 사용하여 리턴 타입을 명확하게 합니다. 앞에서 언급했듯이, 이것은 함수 타입의 필수 부분이므로, 함수가 값을 반환하지 않는다면 그 값을 그대로 두는 대신에 void
를 사용할 수 있습니다.
파라미터와 리턴 타입만 함수 타입을 구성합니다. Capture된 변수는 타입에 반영되지 않습니다. 실제로 Capture된 변수는 함수의 “숨겨진 상태”의 일부이며 해당 API를 구성하지 않습니다.
타입 추정
아래의 예제에서 한쪽에 타입있고 다른쪽에 타입이 없는 경우 TypeScript 컴파일러에서 타입을 알아낼 수 있습니다.
|
|
이를 타입추론의 한 형태 인 “컨텍스트 타입 지정”이라고 합니다. 이렇게하면 프로그램 입력을 위한 노력이 줄어들수 있습니다.
Optional 과 Default 파라미터
TypeScript에서는 모든 파라미터가 함수에 필요하다고 가정합니다. 이것은 여러분이 null
또는 undefined
를 제공할 수 없다는 것을 의미하는 것이 아니라, 컴파일러는 함수가 호출 될 때 사용자가 각 파라미터에 값을 입력했는지 확인한다는 의미입니다. 컴파일러는 또한 이 파라미터들이 함수에만 전달되는 파라미터라고 가정합니다. 즉, 함수에 주어진 파라미터의 수는 함수가 예상하는 파라미터의 수와 일치해야합니다.
|
|
JavaScript에서 모든 파라미터는 선택 사항이며 사용자가 적합하다고 생각되는 파라미터를 그대로 둘 수 있습니다. 그렇게할때 그 파라미터는 undefined
입니다. TypeScript에서 이러한 Optional 파라미터는 파라미터 끝에 ?
를 추가하면 가능합니다. 예를 들어 위의 lastName
파라미터를 Optional로 사용 하고자 한다고 가정해 보겠습니다.
|
|
모든 Optional 파라미터는 Required 파라미터 다음에 나와야 합니다. lastName
보다는 firstName
을 Optional 파라미터로 변경하려고 한다면 함수에서 파라미터의 순서를 변경해야 하며, 목록의 firstName
을 마지막으로 이동해야 합니다.
TypeScript에서 사용자가 파라미터를 입력하지 않거나 파라미터 값 대신 undefined
를 전달해도 파라미터가 할당 될 값을 Default 값으로 설정할 수 있습니다. 이를 Default-initialized 파라미터라고 합니다. 그렇기 때문에 아래 코드와 같이 lastName
의 Default 값을 "Smith"
로 설정할 수 있습니다.
|
|
Required 파라미터 다음에 오는 Default-initialized 파라미터는 Optional 파라미터처럼 취급되며 Optional 파라미터처럼 해당 함수를 호출할 때 생략할 수 있습니다. 이것이 Default-initialized 파라미터가 Optional 파라미터와 공유하는 특성입니다.
|
|
그리고
|
|
(firstName: string, lastName?: string) => string
같은 타입을 공유합니다. lastName
의 Default 값은 파라미터가 Optional 이라는 사실만 남겨두고 타입이 사라집니다.
일반 Optional 파라미터와 달리 Default-initialized 파라미터는 Required 파라미터 다음에 올 필요가 없습니다. Default-initialized 파라미터가 Required 파라미터 앞에 오는 경우 사용자는 명시적으로 undefined
를 전달하여 Default-initialized된 값을 가져 오도록 해야합니다. 예를 들어, 우리는 firstName
의 기본 초기화(Default initializer)만 사용하여 마지막 예제를 작성할 수 있습니다.
|
|
나머지(Rest) 파라미터
Required 파라미터, Optional 파라미터 및 Default-initialized 파라미터는 모두 한 가지 공통점이 있습니다. 즉, 한 번에 하나의 파라미터에 대한 지정입니다. 때로는 여러 파라미터를 그룹으로 사용하거나 함수가 궁극적으로 취할 파라미터의 수를 모를 수 있습니다. JavaScript에서는 모든 함수 본문에서 볼 수있는 arguments
변수를 사용하여 파라미터를 직접 사용할 수 있습니다.
TypeScript에서는 이러한 파라미터들을 모아서 함께 변수로 대입할수 있습니다.
|
|
Rest 파라미터는 무한한 개수의 Optional 파라미터로 취급됩니다. Rest 파라미터에 인수를 전달할 때 원하는 만큼의 파라미터를 입력할수 있습니다. 그리고 또한 아무것도 입력하지 않아도 됩니다. 컴파일러는 줄임표 (...
)로 입력된 이름에 나머지 파라미터들을 배열로 만들어 입력합니다. 이 배열 파라미터는 함수 내부에서 사용할 수 있습니다.
줄임표(...
) 부호는 Rest 파라미터가있는 함수의 타입에도 사용됩니다.
|
|
this
JavaScript에서 this
를 사용하는 법을 배우는 것은 기본 통과 의례중 하나입니다.
TypeScript는 JavaScript의 상위 집합이기 때문에 TypeScript 개발자는 this
를 사용하는 방법과, 올바르게 사용되지 않는 지점을 찾는 방법을 배워야합니다. 다행히 TypeScript를 사용하면 두 가지 기술을 사용하여 잘못된 용도를 잡을 수 있습니다. this
가 JavaScript에서 어떻게 작동하는지 알아야 할 필요가 있다면, Yehuda Katz의 JavaScript 함수 호출및 “this”의 이해를 먼저 읽으십시오. Yehuda의 포스트는 this
의 내부 동작을 잘 설명하므로 여기서는 기본 내용만 다룰 것입니다.
this
와 화살표 함수(Arrow function)
JavaScript에서 this
는 함수가 호출 될 때 설정된 변수입니다. 이것은 매우 강력하고 유연한 기능이지만 함수가 실행될 때 항상 알 필요는 없습니다. 이것은 특히 함수를 반환하거나 함수를 인수로 전달할 때 혼란스러울 수 있습니다.
예제를 살펴 보겠습니다.
|
|
createCardPicker
는 그 자체로 함수를 반환하는 함수입니다. 예제를 실행하려고 하면 예상되는 경고 상자 대신 오류가 발생합니다. 이것은createCardPicker
에 의해 생성된 함수에서 사용되는 this
가 deck
객체 대신에 window
로 설정되기 때문입니다. 왜냐하면 우리는cardPicker()
자신을 호출하기 때문입니다. 이와 같은 최상위 비 메소드 호출 구문은 this
가 window
를 사용합니다. (주의 : 엄격 모드에서 this
는 window
보다는undefined
가 될 것입니다).
사용할 함수를 반환하기 전에 함수가 올바른 this
에 바인딩되어 있는지 확인하면 이 문제를 해결할 수 있습니다. 이렇게하면 나중에 함수가 사용되는 방법에 관계없이 원래의 deck
객체를 바라보게 됩니다. 이를 위해 함수 표현식을 변경하여 ECMAScript 6 Arrow 함수 구문을 사용합니다. Arrow 함수는 호출된 곳이 아닌 함수가 생성된 곳의 this
를 사용합니다.
|
|
더 좋은 점은 컴파일러에 --noImplicitThis
플래그를 넘겨 주면 실수 했을 때 경고를 합니다. 이것은 this.suits[pickedSuit]
의this
가 any
타입이라는 것을 나타냅니다.
this
파라미터
불행히도,this.suits[pickedSuit]
의 타입은 여전히 any
입니다. 왜냐하면 this
는 객체 리터럴 내부의 함수 표현식에서 나온 것이기 때문입니다. 이 문제를 해결하기 위해 명시 적으로 this
파라미터를 제공할 수 있습니다. this
파라미터는 함수의 파라미터 목록에서 처음 나오는 가짜 파라미터입니다.
|
|
위의 예제 인 Card
와 Deck
에 몇 가지 인터페이스를 추가하여 타입을 더 명확하고 쉽게 재사용할 수있게 해보겠습니다.
|
|
이제 TypeScript는 createCardPicker
가 Deck
객체에서 호출 될 것으로 예상합니다. 즉,this
는 any
가 아닌 Deck
타입입니다. 따라서 --noImplicitThis
는 에러를 발생시키지 않습니다.
콜백에서의 this
파라미터
나중에 호출 하는 함수를 라이브러리에 전달할 때 콜백에서 this
를 사용하면 오류가 발생할 수도 있습니다. 콜백을 호출하는 라이브러리가 정상 함수처럼 호출하기 때문에 this
는 undefined
가 됩니다. 어떤 작업으로 콜백 에러를 막기 위해 this
파라미터를 사용할 수 있습니다. 먼저 라이브러리 작성자가 콜백 유형에 this
를 사용하여 annotate를 추가해야합니다.
|
|
this: void
는 addClickListener
가 onclick
이 this
타입을 필요로하지 않는 함수라는 것을 의미합니다. 둘째, this
를 사용하여 호출 코드에 annotate를 달아야 합니다.
|
|
this
가 annotate 처리되어 있으면, onClickBad
는 반드시 Handler
의 인스턴스에서 호출되어야 한다는 것을 기술해야 합니다. 그러면 TypeScript는 addClickListener
에 this: void
가 있는 함수가 필요하다는 것을 탐지합니다. 오류를 수정하려면 this
의 타입을 아래와 같이 변경해야 합니다.
|
|
onClickGood
는 this
타입을 void
로 지정했기 때문에 addClickListener
로 넘겨 줄 수 있습니다. 그것은 this
와 this.info
를 사용할 수 없다는 것을 의미합니다. 둘 다 원한다면 Arrow 함수를 사용해야합니다.
|
|
이것은 Arrow 함수는 this
를 Capture하지 않기 때문에 효과적입니다. 그래서 항상 this: void
를 기대하는 것으로 넘겨 줄 수 있습니다. 단점은 타입 핸들러의 객체별로 하나의 Arrow 함수가 생성된다는 것입니다. 메소드는 한번 작성되어 Handler의 프로토 타입에 Attach 됩니다. 그리고 핸들러 타입의 모든 객체에 공유됩니다.
Overloads
JavaScript는 본질적으로 매우 동적인 언어입니다. 단일 JavaScript 함수가 전달된 파라미터의 모양을 기반으로 여러 타입의 객체를 반환하는 경우는 일반적인 방법입니다.
|
|
여기서 pickCard
함수는 사용자가 무엇을 전달했는지에 따라 두 가지 다른 것을 반환합니다. 사용자가 Deck
를 나타내는 객체를 전달하면 함수가 Card
를 선택합니다. 사용자가 Card
를 선택하면 우리는 그들이 선택한 Card
를 알려줍니다. 이것을 타입 시스템에 어떻게 설명해야 할까요?
대답은 Overload 목록과 동일한 함수에 대해 여러 함수 타입을 제공하는 것입니다. 이 목록은 컴파일러가 함수 호출을 해결하는 데 사용할 것입니다. pickCard
가 받아들이는 것과 그것이 반환하는 것을 설명하는 Overload 리스트를 생성해 보겠습니다.
|
|
위와 같은 변경으로 Overload는 이제 pickCard
함수에 대한 타입이 체크된 호출을 제공합니다.
컴파일러가 올바른 타입 체크를 선택하도록 하기 위해 기본 JavaScript와 비슷한 과정을 거칩니다. Overload 목록을 보고 제공된 파라미터로 함수를 호출하는 첫 번째 Overload 시도를 계속합니다. 일치하는 항목이 있으면 이 Overload를 올바른 Overload로 선택합니다. 이런 이유 때문에 Overload 요청에 따라 가장 구체적인 것에서 덜 구체적인 것 순으로 넘어갑니다.
function pickCard(x): any
는 오버로드 리스트의 일부가 아니므로 객체가 있는 Overload, 숫자가 있는 Overload 두 가지 Overload만 있습니다. pickCard
를 다른 파라미터 유형과 함께 호출하면 오류가 발생합니다.
이 내용은 나중에 참고하기 위해 제가 공부하며 정리한 내용입니다.
의역, 오역, 직역이 있을 수 있음을 알려드립니다.
This post is a translation of this original article [https://www.typescriptlang.org/docs/handbook/functions.html]
참고
- TypeScript 핸드북 1 - 기본 타입
- TypeScript 핸드북 2 - 변수 선언
- TypeScript 핸드북 3 - 인터페이스
- TypeScript 핸드북 4 - 클래스
- TypeScript 핸드북 5 - 함수
- TypeScript 핸드북 6 - Generic
- TypeScript 핸드북 7 - Enum
- TypeScript 핸드북 8 - 타입 유추
- TypeScript 핸드북 9 - 타입 호환성
- TypeScript 핸드북 10 - 고급 타입
- TypeScript 핸드북 11 - Symbol
- TypeScript 핸드북 12 - Iterator와 Generator