Readonly array의 Array.includes 메서드의 인자 타입 문제

khundi·2023년 11월 29일
0

재즈밋 프로젝트를 진행하며 생겼던 타입스크립트의 문제 상황을 기록하고 공유하는 글입니다.

더 좋은 해결방법이나 잘못된 정보가 있다면 댓글로 알려주세요.

문제 및 원인 분석

이미지 업로드 요청을 구현하는 중 확장자를 제한하는 로직을 짜고 있었습니다. 여기서 png, jpg, jpeg의 확장자만 허용하도록 정했고, 이를 따로 readonly array로 관리하였습니다. 그리고 Array.includes 메서드로 확장자를 검사하면서 문제가 생겼습니다.

const ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg'] as const; // const ALLOWED_EXTENSIONS: readonly ["png", "jpg", "jpeg"]

ALLOWED_EXTENSIONSconst assertions으로 상수화하였고,

function uploadImage(image: File) {
    const extension = image.split('.').pop();
  
	if(extension && ALLOWED_EXTENSIONS.includes(extension)) { // Argument of type 'string' is not assignable to parameter of type '"png" | "jpg" | "jpeg"'.ts(2345)
      //                                        ^^^^^^^^^
    }
  
    // ...
}
Argument of type 'string' is not assignable to parameter of type '"png" | "jpg" | "jpeg"'.ts(2345)

에러 내용은 "string 타입은 type '"png" | "jpg" | "jpeg"'에 할당할 수 없다"고 나옵니다.

왜 이런 에러가 나오게 되었을까 살펴보니

위와 같이 타입스크립트는 Array, ReadonlyArrayincludes 메서드를 정의할 때 첫번째 인자를 Array의 제네릭을 따르도록 정의되어 있습니다.

ALLOWED_EXTENSIONS"png" | "jpg" | "jpeg"의 유니온 타입이었고, 첫 번째 인자인 extension은 string이었기 때문에 에러가 났던 것입니다.

해결 방법

해결 방법은 매우 다양하지만 몇가지만 다루겠습니다.

1. Type Annotation

const assertions가 아닌 Type Annotation을 활용하여 ALLOWED_EXTENSIONSReadonlyArray<string>으로 상수화를 할 수 있습니다.

const ALLOWED_EXTENSIONS: Readonly<string> = ['png', 'jpg', 'jpeg'];

includes 앞에 배열 제네릭 타입이 string이 되고 extensionstring이므로 에러가 나지 않습니다.

2. Type Predicates

Type Predicates을 활용하여 extension의 타입을 좁혀줍니다.

function isInArray<T>(array: ReadonlyArray<T>, value: unknown): value is T {
  return array.includes(value as T);
}

2-1. Type Assertion

extenstion의 타입을 assertion합니다.

if(extension && ALLOWED_EXTENSIONS.includes(extension as typeof ALLOWED_EXTENSIONS[number])) { 

그외

  • includes 인터페이스 재정의
  • includes를 사용하지 않고 some 메서드로 값 확인

마치며

위 방법 모두 100% 해결했다고 볼 수 없을 것 같습니다.

개중에 1번 방법이 적절하다고 생각됩니다. 배열의 읽기 전용 타입을 지킬 수 있고 추가 로직이 없으며 includes의 인터페이스를 그대로 유지할 수 있기 때문입니다.

profile
안녕하세요. 웹 프론트엔드 개발자 전성훈입니다.

0개의 댓글