TypeScript 팁: 조건부 유형을 사용하여 오버로드를 리팩터링합니다.

소개



며칠 전에 코드를 리팩토링하다가 다음과 같은 것을 발견했습니다.

interface IRawUser { first_name: string email: string, id: number, } interface User { name: string, email: string print: () => void } declare function userFactory(rawUser: IRawUser): User; function mapRawToUserObject(rawShow: IRawUser[]): User[]; function mapRawToUserObject(rawShow: IRawUser): User; function mapRawToUserObject(rawShow: IRawUser | IRawUser[]): User | User[] { if (rawShow instanceof Array) { return rawShow.map((raw) => userFactory(raw)); } return userFactory(rawShow); } 


함수mapRawToUserObject는 오버로드를 사용하여 다음을 표현합니다. IRawUser[]의 배열로 호출하면 User[]의 배열을 반환하고, 단일IRawUser로 호출하면 단일User을 반환합니다. ) .

너무 복잡하지는 않지만 메서드 정의를 리팩토링할 수 있는 좋은 기회인 것 같습니다.

조건부 유형 생성.



메서드가 받는 입력 유형을 결정해야 합니다.

살펴봐야 할 가능한 입력은 IRawUserIRawUser[]입니다.

type MapRawResult<T extends IRawUser | IRawUser[]> = any; 


그런 다음 조건부 논리를 추가합니다.

type MapRawResult<T extends IRawUser | IRawUser[]> = T extends IRawUser ? User : User[] 


In plain words: If T is assignable to IRawUser return User otherwise return User[]





우리의 방법에 조건부 유형을 적용합니다.



지금 우리의 방법은 다음과 같습니다.

function mapRawToUserObject(rawShow: IRawUser[]): User[]; function mapRawToUserObject(rawShow: IRawUser): User; function mapRawToUserObject(rawShow: IRawUser | IRawUser[]): User | User[] { // implementation } 


이제 오버로드를 제거하고 일반 매개변수( T extends IRawUser | IRawUser[] )를 추가하고 반환 유형을 최근에 만든 것으로 바꿀 수 있습니다.

type MapRawResult<T extends IRawUser | IRawUser[]> = T extends IRawUser ? User : User[] function mapRawToUserObject<T extends IRawUser | IRawUser[]>(rawShow: T): MapRawResult<T> { // implementation } 


이 시점에서 typescript는 우리가 반환하는 값의 유형에 대해 불평할 것입니다.



컴파일러는 우리가 반환하는 값의 유형을 유추할 수 없습니다. 이에 대한 해결책은 키워드 as 를 사용하여 예상되는 유형을 명시적으로 표현하는 것입니다.

최종 방법은 다음과 같습니다.

function mapRawToUserObject<T extends IRawUser | IRawUser[]>(rawShow: T): MapRawResult<T> { if (rawShow instanceof Array) { return rawShow.map((raw) => userFactory(raw)) as MapRawResult<T> } return userFactory(rawShow) as MapRawResult<T> } 


결론



일부 값으로 시도하면 메서드가 오버로드를 사용하지 않고 이전과 동일하게 동작하는 방식을 볼 수 있습니다.





보너스로 IRawUser | IRawUser[]에 대한 유형 별칭을 사용하고 코드를 더 깔끔하게 만들 수 있습니다.

type MapRawArg = IRawUser | IRawUser[]; type MapRawResult<T extends MapRawArg> = T extends IRawUser ? User : User[]; function mapRawToUserObject<T extends MapRawArg>(rawShow: T): MapRawResult<T> { // ... } 


추가 자료


  • Conditional Types
  • Type Assertions
  • Generics
  • TypeScript Playground
  • 좋은 웹페이지 즐겨찾기