본문 바로가기

javascript/typescript

[타입스크립트] 유틸리티 타입 part 1

이전에 언급했던 Partial 유틸리티 타입처럼, 타입스크립트 자체적으로 지원해주는 많은 유틸리티 타입이 존재한다. 

이번에는 이렇게 프로그래머를 도와주는 유틸리티 타입에 대해 공부할 예정이다.

 

 

 

Documentation - Utility Types

Types which are globally included in TypeScript

www.typescriptlang.org

 

# Partial<Type>

Type 집합의 프로퍼티를 선택적으로 설정한 타입을 생성한다. 

유저가 계정을 생성하는 경우를 생각해보자. 우리는 계정을 생성할 때 ID, 비밀번호, 이름, 이메일 등의 정보를 필수적으로 수집한다. 이 정보들은 반드시 필요한 값들이므로, null 또는 undefined로 설정되어서는 안된다. 이 경우, 이러한 프로퍼티들을 선택적으로 설정하는 것은 적합하지 않다.

 

 

이런 상황에 대해 Partial을 사용하면 위 프로퍼티를 선택적으로 받을 수 있어, 추가적인 엔티티가 필요없다.

 

 

위 update 함수는 Partial<UserEntity> 타입을 매개변수로 가진다. updateUser 함수는 save에 UserEntity의 일부 인자(위의 경우 name과 password)만을 전달하여 일부 요소만을 업데이트 할 수 있다. 유저가 수정한 요소만 서버로 받아오는 경우 유용할 수 있을 것 같다.

 

Partial<T>의 구현

 

# Required<Type>

 

Partial<Type>의 반대 버전이다. Type 집합의 모든 프로퍼티를 필수로 만든다. 유저의 정보중 일부가 선택적으로 정의되지만, 계정을 생성할 때는 선택적인 정보 역시 모두 필요한 상황에서는 사용을 고려해도 괜잖겠다. 

유저 정보 중 일부는 선택적으로 나타난다.

 

서버 내부에서는 모든 정보를 요구하므로, 이를 설정해볼 수도 있다.

 

값이 하나라도 없다면 에러가 발생한다.

 

 

 

Required<T>의 구현

# Readonly<Type>

대상의 모든 프로퍼티를 readonly로 변환한다. Object.freeze를 거치는 것과 유사하다.

객체는 참조에 의해 작동하기 때문에 함수로 인자로 넘긴 후 프로퍼티를 변경하면 실제 객체에게도 영향을 준다.

만약 이런 동작을 막고 싶다면 Readonly의 사용을 생각해 볼 수도 있다.

 

예시 코드

 

changeTitle을 수행한 이후 todo.title 값이 달라졌다.

 

Readonly 유틸리티 타입을 이용한 경우. 값이 변경되지 않는다.

 

사실 타입스크립트에는 readonly라는 키워드가 존재한다. 해당 키워드는 클래스 등에서 사용되며, 해당 키워드가 선언된 프로퍼티에 대해서는 생성자를 통해 값이 할당된 이후에는 값의 변경이 불가능하도록 막는다. 

 

readonly 키워드

앞서 언급했듯이 Readonly는 입력된 타입의 모든 프로퍼티를 readonly로 변환하여 돌려주는 방식으로 동작하기 때문에 readonly 키워드보다는 C++의 const처럼 내부에서 값을 변경하지 못하도록 하는 쪽에 가깝다. C++에서는 함수/메서드에서 인자로 전달되는 대상의 값을 변경하지 못하게 막기 위해 const 키워드를 사용한다. 그러나 자바스크립트 및 타입스크립트는 이런 동작을 언어 자체적으로 지원하지 않는다.

물론 Object.defineProperty와 같은 함수를 이용하여 프로퍼티의 특성을 변경할 수 있기는 한데, 이는 각각의 프로퍼티의 값을 변경하지 못하도록 플래그를 세우는 방식으로 readonly 키워드에 가까운 동작이지, C++에서 함수에 레퍼런스를 전달할 때 해당 레퍼런스의 각종 값을 변경할 수 없도록 제한하도록 만들어주는 const 키워드와는 다르다. 

그렇기에 기존에는 내부에서 값을 변경하지 못하도록 강제하는 방법이 마땅치 않았는데, Readonly 덕분에 이런 동작을 손쉽게 구현할 수 있게 되었다.

Readonly<Type>의 내부 구현 방식.

# Record<Keys, Type>

Keys에 포함된 각각의 key에 대한 Type 프로퍼티가 정의되는 타입을 반환한다. 흔히 아는 딕셔너리 자료구조를 객체에 대해 적용한 느낌이다. 기존의 객체가 다양한 키를 사용할 수 있었음에 비해, Record의 등장으로 Key의 타입을 제한할 수 있다. Keys에는 string, number, symbol 타입 및 해당 타입에서 파생되는 리터럴의 union이 올 수 있는데, string으로 지정하는 경우 number및 symbol 타입에 대해 딱히 제한을 두지 않았다.

 

Record 타입을 사용한 예시

위 코드는 상품의 id를 의미하는 itemId 와 상품의 정보를 나타내는 Item을 Record 타입을 이용하여 연결한 것이다.

앞서 언급한 것처럼 리터럴의 union에 대해서도 Record의 key를 지정할 수 있다. 이 경우, 해당 union에 정의된 모든 리터럴이 key로 사용되어야 한다. 리터럴들은 타입이 같지 않더라도 무방하다.

 

리터럴에 대한 Record도 가능

Record 유틸리티 타입의 경우, key-value로 오는 값을 제한 할 수 있는 객체를 만드는데 의의를 두자. 

 

Record<Keys, Type>의 내부 구현

결론

  • Partial : 대상의 프로퍼티를 모두 선택적으로 설정한 타입을 반환한다.
  • Required : 대상의 프로퍼티를 모두 필수적으로 설정한 타입을 반환한다.
  • Readonly : 대상의 프로퍼티를 모두 readonly로 설정한 타입을 반환한다. 매개변수에 대한 const처럼 작동한다.
  • Record : Keys및 Type에 대해 제약이 설정된 Key-Type pair의 집합에 대한 타입을 반환한다.

모두 "타입"이라는 점을 기억하자.