명시적 this
TypeScript에서 함수에서의 this 사용을 아래의 간단한 예제와 함께 살펴보도록 하겠습니다.
interface Cat {
name: string;
age: number;
}
const cat: Cat = {
name: "rati",
age: 5,
};
function hello(message: string) {
console.log(`hello ${this.name}, ${message}`); // 타입 에러 발생
}
hello.call(cat, "You are pretty awesome");
인터페이스 Cat은 내부에서 속성에 대한 내용이 작성되어 있으며, 변수 cat는 인터페이스 Cat를 사용하여 타입을 명시하고 객체를 할당받고 있습니다.
함수 hello는 매개 변수 message에 인수로 들어오는 데이터의 타입을 명시해 주었으며, 내부에서 콘솔 로그를 통해 문자 데이터를 출력하고 있는데 this.name을 사용하고 있습니다.
*일반 함수에서의 this는 호출 위치에서 this가 정의됩니다.
call 메서드를 사용했기 때문에 객체 cat에서 해당 함수를 메서드처럼 호출했기 때문에 this는 호출 위치가 cat이 됩니다.
코드를 실행하면 런타임에서는 hello rati, You are pretty awesome이 정상적으로 나타나지만 타입스크립트의 코드에서는 타입 에러가 발생하게 됩니다. 이는 this가 어떤 타입의 데이터가 될지 확실하지 않음으로 인한 타입 에러입니다.
function hello(this: Cat, message: string) {
console.log(`hello ${this.name}, ${message}`);
}
때문에 this가 무엇인지 정확하게 타입스크립트에 알려줄 필요가 있으며, 위와 같이 this의 타입을 명시해줄 수 있습니다.
위와 같이 명시된 this는 인터페이스 Cat을 사용하여 타입을 명시해 주었고, 인터페이스 Cat은 변수 cat의 객체 타입으로 명시되고 있습니다. 결과적으로 this는 cat이라는 객체 데이터가 될 거라고 명시적으로 this의 타입을 지정해줄 수 있습니다.
*명시적 this 타입 지정은 매개 변수처럼 보일 수 있지만, 함수 내부에서 사용하는 this의 타입을 명시적으로 지정하는 것입니다.
오버로딩( Overloading )
TypeScript의 함수에서 사용할 수 있는 오버로딩에 대해서 아래의 간단한 예시와 함께 살펴보도록 하겠습니다.
예시 1
function add1(a: string, b: string) {
return a + b;
}
function add2(a: number, b: number) {
return a + b;
}
add1("hello", "world!");
add2(1, 2);
add1("hello", 2); // 타입 에러 발생
add2("hello", 2); // 타입 에러 발생
함수 add1과 add2는 매개 변수 a와 b를 각각 위와 같이 타입을 명시하고 있습니다. 함수 add1의 경우 문자 데이터만 인수로 받아 연산 후 반환하고, 함수 add2의 경우 숫자 데이터만 인수로 받아 연산 후 반환하기 때문에 문자와 숫자를 인수로 전달하는 경우 타입 에러가 발생하게 됩니다.
예시 1번에서는 발생한 타입 에러를 수정하기보다 함수 add1과 함수 add2가 동일한 구조로 매개변수의 타입 명시만 달라 위와 같이 두 개의 함수로 관리할 수 있지만 함수의 오버로딩을 이용하여 보다 단순하게 함수를 관리할 수 있다 정도로 마무리하고 다음 예시로 넘어가겠습니다.
예시 2
function add(a: string, b: string): string; // 1번
function add(a: number, b: number): number; // 2번
function add(a: any, b: any) { // 3번
return a + b;
}
add("hello", "world!");
add(1, 2);
add("hello", 2); // 타입 에러 발생
add("hello", 2); // 타입 에러 발생
예시 2의 경우 동일한 함수명을 가진 세 개의 함수 add가 위와 같이 작성되어 있으며 오버로딩이 된 함수입니다.
3번의 경우 함수명이 add이며, 매개 변수 a와 b의 타입으로 any가 명시되어 있으며, a와 b를 연산하여 반환하고 있습니다.
1번의 경우 3번과 동일한 함수명과 매개 변수 a와 b의 타입으로 string이 명시되어 있으며, 반환하는 값의 타입도 string으로 명시되어 있습니다.
2번의 경우 1번과 구조가 동일하지만 타입만 number로 명시되어 있습니다.
즉, 함수의 오버로딩 개념을 사용하게 되면 함수 add는 아래의 두 가지 타입의 함수로 존재하게 되는 것입니다.
- 1번 타입 : 매개 변수가 string 타입인 함수 add
- 2번 타입 : 매개 변수가 number 타입인 함수 add
3번의 경우 별도의 타입이 아닌 함수의 구현 부분입니다. 3번의 매개 변수는 any 타입으로 명시되어 있으며 any 타입을 통해 1번 타입의 string이나 2번 타입의 number가 들어올 수 있게 되는 것입니다.
이렇게 함수의 오버로딩을 사용하면 예시 1번처럼 로직이 동일한 함수를 함수명만 다르게 작성하여 사용하는 것이 아닌 동일한 이름의 함수를 매개 변수의 타입만 다르게 명시하여 사용할 수 있게 됩니다.
예시 2의 타입 에러의 경우 첫 번째 매개 변수로 string이 들어왔으면 두 번째 매개 변수도 string이 들어오거나, 첫 번째 매개 변수에 number가 들어왔으면 두 번째 매개 변수도 number가 들어와야 하는데 각각의 매개 변수의 타입이 서로 일치하지 않기 때문에 위와 같은 타입 에러 메시지가 나오게 되는 것입니다.
정리를 해보면 1번과 2번 코드의 경우 함수의 타입을 선언하는 코드이고, 3번은 함수의 구현부로 타입 선언된 1번과 2번 중 하나를 선택해서 사용할 수 있다의 개념입니다.
*3번의 매개 변수의 타입이 any로 명시된 것은 실제로 어떤 타입이 들어와도 상관없다는 것이 아닌 오버 로딩된 함수들 중 하나가 들어와도 상관없다의 의미로 사용된 것입니다.
'TypeScript' 카테고리의 다른 글
제네릭 (Generic) - Updated keyof 제약 조건 (0) | 2023.01.03 |
---|---|
클래스 (0) | 2023.01.02 |
타입 별칭(Alias) (0) | 2023.01.02 |
인터페이스 (Interface) (2) | 2023.01.02 |
타입 가드(Guard) (0) | 2023.01.02 |