TypeScript에 대한 간단한 정리 - 08. 함수 - 명시적 this 타입

2024. 12. 20. 17:39Programming Language/Node.js

타입스크립트에서는 함수 내부에서 사용되는 this 키워드의 타입을 명시적으로 정의할 수 있다.

원래 자바스크립트에서는 함수의 this를 호출 방식에 따라서 동적으로 결정하는데 타입스크립트에서는 이걸 명시적으로 지정해서 호출 할때 this의 타입을 고정 시킬 수 있다.

 

기본적으로 선언하는 방식은 

function 함수명(this: 타입(보통 객체의 타입), ...): ReturnType {
  // 함수 내용
}

과 같이 사용한다.

 

인터페이스에서 this에 대해서 한번 보자면 

이렇게 인터페이스를 생성한 다음에 함수에서 

이렇게 this를 사용하고 싶다고 생각해보자.

 

이럴때 사용하는 메서드가 call이란 메서드인데 call메서드는 자바스크립트의 모든 함수에서 사용할 수 있는 메서드로 함수를 호출 할때 첫번째 인자로 객체를 전달하면 그 객체에서 해당 함수를 호출한것 처럼 호출해준다.

예를 들면 

이렇게 함수를 선언하고 그 함수명에 call 메서드를 붙인 다음에 첫번째 인자로 객체를 전달하고, 두번째 인자로는 함수에서 받을 매개변수를 차례로 전달해주면 

이렇게 obj라는 객체에서 testFunc 함수가 호출되어 this.name이라는 값은 obj.name과 동일하게 사용이 된다.

//call 메서드 사용 

함수명.call(객체, 전달인자1, 전달인자2 ...);

그런데 만약 여기서 객체에 name이란 속성이 존재하지 않는 객체를 담아 던진다면 

컴파일 에러는 발생하지 않으나.

이렇게 값이 undefined로 출력되게 된다.

이는 this를 명시적으로 설정하지 않으면 

이렇게 any타입으로 인식하기 때문이고 이 this는 any 타입이 되었기에 전역 객체인 window를 참조하게 되면서 없는 속성인 name을 undefined로 출력하게 되는 것이다.

 

이런 문제를 방지 하는 방법이 this를 명시적으로 설정해주는 것이다.

이렇게 함수의 첫번째 매개변수로 this를 지정하고 그 타입을 설정 하고자 하는 타입으로 설정해주면 명시적으로 this의 타입을 설정할 수 있다.

이때 하나 주의 해야할 점은 타입스크립트는 구조적 서브타이핑(Structural Subtyping)이란걸 사용하는데 이 구조적 서브타이핑은 타입이 구조적으로 동일하다면 타입의 이름이 다르더라도 서로 호환 가능하게 사용이 되게 하는 방식이다.

그래서 

이렇게 구조적으로 동일한 방식의 인터페이스가 존재한다면 

이렇게 서로 다른 타입의 객체를 생성하더라도 

A타입을 받는 this가 이 B타입의 객체를 문제 없이 받아준다는 의미이다.

 

그런데 만약 B에 name 타입이 없고 

아래와 같이 코드가 작성되어 있다 하더라도 컴파일 에러를 발생시키지는 않지만 이전처럼 undefined를 출력한다.

그 이유는 우리가 이전에 타입을 명시적으로 지정하면 컴파일러는 이에 대해서 의문을 가지지 않기 때문이다.

그렇기에 컴파일러는 this로 전달된 obj2라는 객체가 A의 타입을 갖고 있겠지라고 생각하고 처리해주면서 컴파일 에러를 발생시키지는 않는것이다.

 

반면 this를 A로 설정한 경우에 함수의 내부에서 A의 속성이 아닌 B의 속성을 this로 접근하려고 한다면 

컴파일러는 어떤 객체가 전달되었건 this는 A타입이라고 생각하기 때문에 이렇게 A 타입에선 이런 속성이 존재하지 않는다고 컴파일 에러를 발생시킨다.

 

타입스크립트에서 this 를 사용하는 이유는 타입 안전성, 가독성, 함수의 의도 명확화, 오류 방지 등 이라고 하는데 그냥 this의 타입을 보다 정확하게 추적하고, 코드의 의도를 명확하게 표현하기 위해서라고 생각해두자.

 

이게 자주 사용되는 방식은 아님을 알아두자.