一.타입
함수의 타입은 두 부분으로 나뉩니다:
-
파라미터: 각 파라미터의 타입
-
리턴값: 리턴값의 타입
예를 들어:
//具名 함수
function add(x: number, y: number): number {
return x + y;
}
// 匿名 함수
let myAdd = function(x: number, y: number): number { return x + y; };
타입이 있는 함수 선언은 함수의 타입 정보를 충분히 표현할 수 있지만, 재사용할 수 없습니다. 그렇다면 함수의 타입을 재사용하는 방법이 있을까요?
있습니다. 타입을 추출하면 재사용할 수 있습니다. 일단 타입 기술이라고 부르겠습니다
타입 기술
화살표 함수 구문을 통해 함수의 타입을 기술할 수 있습니다:
let myAdd: (x: number, y: number) => number =
function(x: number, y: number): number { return x + y; };
화살표 (=>) 의 왼쪽은 파라미터와 그 타입이며, 오른쪽은 리턴값 타입입니다. 이들은 모두 구문 구조의 일부이며, 생략 불가입니다:
// 리턴값 없음
let log: (msg: string) => void = function(msg) {
console.log(msg);
};
// 파라미터 없음
let createLogger: () => object = function() {
return { log };
};
// 파라미터도 리턴값도 없음
let logUa: () => void = log.bind(this, navigator.userAgent);
P.S.위 예시에서는 타입을 하나만 선언한 것에 주목하십시오. 오른쪽 익명 함수의 타입은 왼쪽 타입 선언에 기반하여 자동으로 추론될 수 있기 때문입니다. 이를 문맥 타입 추론 (contextual typing) 이라고 합니다
또한, 타입 기술 내의 파라미터 이름은 가독성을 위한 것이며, 타입 기술 중의 파라미터 이름이 실제 파라미터 이름과 일치할 필요는 없습니다. 예를 들어:
let myAdd: (baseValue: number, increment: number) => number =
function(x: number, y: number): number { return x + y; };
P.S.실제로는, 함수 타입을 기술하는 또 다른 방법이 있습니다: 인터페이스입니다. 상세는 인터페이스_TypeScript 노트 3 참조
二.파라미터
옵션 파라미터
JavaScript 에서 파라미터는 기본적으로 모두 옵션입니다 (전달하지 않으면 기본적으로 undefined). TypeScript 에서는 각 파라미터가 필수로 간주됩니다. 옵션 파라미터를 명시적으로 선언하지 않는 한:
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
옵션 프로퍼티 의 구문과 유사하며, 파라미터 이름 바로 뒤의 ? 는 해당 파라미터가 옵션임을 나타내며, 옵션 파라미터는 필수 파라미터 뒤에 나타나야 함 을 요구합니다 (따라서 firstName 을 옵션으로, lastName 을 필수로 하고 싶다면, 파라미터 순서를 변경할 수밖에 없습니다)
디폴트 파라미터
디폴트 파라미터 구문은 [ES 사양](/articles/默认参数和不定参数-es6 笔记 4/#articleHeader3) 과 일치합니다. 예를 들어:
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
의미로 보면, 디폴트 파라미터는 당연히 옵션입니다 (기입하지 않으면 디폴트 값을 사용). 따라서, 디폴트 파라미터는 특수한 옵션 파라미터로 간주할 수 있으며, 타입 기술도 호환됩니다:
let buildName: (firstName: string, lastName?: string) => string;
// 옵션 파라미터
buildName = function(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
};
// 디폴트 파라미터
buildName = function(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
};
양자의 타입은 완전히 일치하므로, 타입 기술은 디폴트 파라미터를 완전히 표현할 수 없습니다 (옵션의 의미만 표현할 수 있으며, 디폴트 값은 손실됩니다)
또 다른 차이는, 디폴트 파라미터는 필수 파라미터 뒤에 나타날 필요가 없다는 것입니다. 예를 들어:
function buildName(firstName = "Will", lastName: string) {
return firstName + " " + lastName;
}
buildName(undefined, "Adams");
undefined 를 명시적으로 전달하여 플레이스홀더로 합니다. 상세는 [三。디폴트 파라미터](/articles/默认参数和不定参数-es6 笔记 4/#articleHeader3) 참조
나머지 파라미터
[ES6 가변장 파라미터](/articles/默认参数和不定参数-es6 笔记 4/#articleHeader2) 의 구문과 일치합니다:
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
나머지 파라미터도 옵션이며, 무제한 수의 옵션 파라미터 에 해당합니다:
Rest parameters are treated as a boundless number of optional parameters.
또한, 타입 기술 중에서도 동일한 구문이 채택되었습니다:
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
三.this
this 는 JavaScript 에서 그다지 쉽게 다루기 어렵습니다. 예를 들어:
class Cat {
constructor(public name: string) {}
meow() { console.log(`${this.name} meow~`); }
}
let cat = new Cat('Neko');
// 클릭으로 트리거되는 log 중에서, name 이 손실됨
document.body.addEventListener('click', cat.meow);
this 의 타입
특별히, TypeScript 는 this 의 타입을 기술할 수 있습니다. 예를 들어:
class Cat {
constructor(public name: string) {}
meow(this: Cat) { console.log('meow~'); }
}
class EventBus {
on(type: string, handler: (this: void, ...params) => void) {/* ... */}
}
new EventBus().on('click', new Cat('Neko').meow);
그 중에서 this 는가짜 파라미터이며, 반드시 첫 번째 파라미터로 나타나야 합니다:
this parameters are fake parameters that come first in the parameter list of a function.
this 도 일반 파라미터와 마찬가지로 타입 체크되며, 유사한 오류를 사전에暴露할 수 있습니다:
Argument of type '(this: Cat) => void' is not assignable to parameter of type '(this: void, ...params: any[]) => void'.
P.S.또한, --noImplicitThis 컴파일 옵션을 유효하게 하여, 모든 this 에 명시적인 타입 선언을 강제 요구할 수 있습니다
四.오버로드
Java 의 오버로드와 유사:
Method Overloading: This allows us to have more than one method having the same name, if the parameters of methods are different in number, sequence and data types of parameters.
(Types of polymorphism in java- Runtime and Compile time polymorphism 에서 인용)
간단히 말하면, 동명 함수의 다른 버전을 공존시킬 수 있습니다. 다른 버전은 파라미터의 차이에 나타납니다:
-
파라미터 수
-
파라미터 순서
-
파라미터 타입
이 3 가지 특징 중 하나라도 다르면 오버로드로 간주됩니다. 모두 같으면, 중복 선언된 메소드 (Duplicate Method) 로 간주되며, 컴파일 오류가 스로우됩니다:
// Java
public class Addition {
// Compile Time Error - Duplicate method sum(int, int)
int sum(int a, int b) {
return a+b;
}
// Compile Time Error - Duplicate method sum(int, int)
void sum(int a, int b) {
System.out.println(a+b);
}
}
TypeScript 에도 오버로드의 개념이 있지만, Java 오버로드와는 몇 가지 차이가 있습니다. 예를 들어:
class Addition {
sum(a: number, b: number): number {
return a + b;
}
sum(a: number[]): number {
return a.reduce((acc, v) => acc + v, 0);
}
}
매우 합리적으로 보이지만, TypeScript 에서는 오류가 보고됩니다:
Duplicate function implementation.
컴파일 결과는 다음과 같습니다 (TypeScript 의 컴파일 오류는 코드 생성에 영향을 주지 않습니다. 상세는 [타입 시스템](/articles/typescript 简介-typescript 笔记 1/#articleHeader6) 참조):
var Addition = /** @class */ (function () {
function Addition() {
}
Addition.prototype.sum = function (a, b) {
return a + b;
};
Addition.prototype.sum = function (a) {
return a.reduce(function (acc, v) { return acc + v; }, 0);
};
return Addition;
}());
JavaScript 는 오버로드를 서포트하지 않기 때문에, (동일 스코프 내의) 메소드는 먼저 선언된 동명 메소드를 덮어씁니다. 함수 시그니처가 같은지 여부와 관계없이. 따라서, TypeScript 에서의오버로드 능력은 제한되며, 타입 상에서만 나타납니다:
function sum(a: number, b: number): number;
function sum(a: number[]): number;
function sum(a, b?) {
if (Array.isArray(a)) {
a.reduce((acc, v) => acc + v, 0);
}
return a + b;
}
마찬가지로, 이러한 오버로드 타입 선언은 컴파일 시에만 작용하므로, [패턴 매칭](/articles/基础语法-haskell 笔记 1/#articleHeader18) 과 유사한 특성도 있습니다:
function sum(a: any, b: any): any;
function sum(a: number, b: number): number;
function sum(a, b) {
return Number(a) + Number(b);
}
// 여기서 value 는 any 타입
let value = sum(1, 2);
위 예시에서는, 먼저 선언된 더 광범위한 any 버전이 정상적으로 매칭되었으므로, 따라서 예상대로 더 정확한 number 버전에 매칭되지 않았습니다.
It looks at the overload list, and proceeding with the first overload attempts to call the function with the provided parameters. If it finds a match, it picks this overload as the correct overload.
따라서, 가장 광범위한 버전을 마지막에 선언해야 합니다:
it's customary to order overloads from most specific to least specific.
아직 댓글이 없습니다