재즈밋 프로젝트를 진행하며 생겼던 타입스크립트의 문제 상황을 기록하고 공유하는 글입니다.
더 좋은 해결방법이나 잘못된 정보가 있다면 댓글로 알려주세요.
이미지 업로드 요청을 구현하는 중 확장자를 제한하는 로직을 짜고 있었습니다. 여기서 png
, jpg
, jpeg
의 확장자만 허용하도록 정했고, 이를 따로 readonly array
로 관리하였습니다. 그리고 Array.includes
메서드로 확장자를 검사하면서 문제가 생겼습니다.
const ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg'] as const; // const ALLOWED_EXTENSIONS: readonly ["png", "jpg", "jpeg"]
ALLOWED_EXTENSIONS
를 const 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
, ReadonlyArray
의 includes
메서드를 정의할 때 첫번째 인자를 Array
의 제네릭을 따르도록 정의되어 있습니다.
ALLOWED_EXTENSIONS
는 "png" | "jpg" | "jpeg"
의 유니온 타입이었고, 첫 번째 인자인 extension은 string
이었기 때문에 에러가 났던 것입니다.
해결 방법은 매우 다양하지만 몇가지만 다루겠습니다.
const assertions
가 아닌 Type Annotation
을 활용하여 ALLOWED_EXTENSIONS
을 ReadonlyArray<string>
으로 상수화를 할 수 있습니다.
const ALLOWED_EXTENSIONS: Readonly<string> = ['png', 'jpg', 'jpeg'];
includes
앞에 배열 제네릭 타입이 string
이 되고 extension
은 string
이므로 에러가 나지 않습니다.
Type Predicates
을 활용하여 extension
의 타입을 좁혀줍니다.
function isInArray<T>(array: ReadonlyArray<T>, value: unknown): value is T {
return array.includes(value as T);
}
extenstion
의 타입을 assertion
합니다.
if(extension && ALLOWED_EXTENSIONS.includes(extension as typeof ALLOWED_EXTENSIONS[number])) {
includes
인터페이스 재정의includes
를 사용하지 않고 some
메서드로 값 확인위 방법 모두 100% 해결했다고 볼 수 없을 것 같습니다.
개중에 1번 방법이 적절하다고 생각됩니다. 배열의 읽기 전용 타입을 지킬 수 있고 추가 로직이 없으며 includes
의 인터페이스를 그대로 유지할 수 있기 때문입니다.