오랜만에 글을 써보는 것 같습니다.
오늘은 대강 추상적으로만 알았던 unknown과 any 타입의 차이에 대해서 알아보겠습니다.
❓any
먼저 원래 쓰던 any에 대해서 알아보겠습니다.
any는 아무거나라는 뜻입니다.
말 그대로 어떤 타입에도 할당이 가능하고 어떤 타입이든 할당할 수 있습니다.
let anyValue: any = '대충 문자열';
let stringValue: string = anyValue;
let numberValue: number = 0;
anyValue = numberValue;
우리가 보던 코드입니다.
보기에도 문제없고 동작도 아주 잘 할 겁니다.
하지만
저는 차이에 관한 글을 보기 전에는 어딘가 문제가 있어 보인다고 생각을 안 해봤습니다.
그런데 anyValue가 string 타입이 아니라 number나 boolean 타입이라면 어떨까요?
JS의 타입 시스템을 더한 TS에서는 오류가 발생합니다.
하지만 런타임에 잡아낼 뿐이고 컴파일 타임에는 잡아내질 않죠.
TS의 장점 중 하나가 컴파일 타임에 타입 에러를 잡아내는 것인데
이러면 TS의 장점 하나가 잘 드러나지 못 합니다.
실제로 오류를 잡아내고 있지 않습니다.
또 다른 예시를 봐보겠습니다.
let anyValue: any = 'abcd';
anyValue.toUpperCase();
위 코드도 마찬가지로 잘 동작할 겁니다.
이것도 위에서 언급한 문제처럼 string 타입이 아닐 때에 toUpperCase()는
오류가 발생해야 정상입니다.
하지만 똑같이 런타임에 나타날 뿐이죠.
any 타입은 거의 만능이나 다름없는 type이긴 합니다.
컴파일 타임에 나타나는 오류를 해결해주는 슈퍼맨?
하지만 저런 류가 아니어도 TS의 목적과는 조금 다른 타입입니다.
any를 사용하면 일반 JS와 다를 바가 없을 정도니까요.
그래서 사용할 때도 어떤 값이 올 지 모를 때 등에 사용하는 타입입니다.
그럼 이젠 any의 문제점을 개선하기 위해 나온 unknown 타입에 대해서 알아보겠습니다!
❓unknown
unknown은 알 수 없는이라는 뜻입니다.
아무거나라는 뜻과는 조금 다르죠?
unknown은 어떤 타입이든 할당이 가능하지만
어떤 타입에 할당을 할 수는 없습니다. 그냥은요.
만약 타입을 알 수 없다라는 거라면 알아내면 그만입니다.
그래서 unknown 타입을 사용하면 type을 검사하는 수고로움이 생깁니다.
하지만 any 대신에 unknown을 사용하는 거라면?
unknown은 TypeScript 3.0부터 나온 any 타입의 대체제 느낌입니다.
unknown을 사용함으로써 any 타입의 문제점을 해결하는 거라면
타입 검사는 코드를 더 깔끔하고 안전하게 만들어주는 겁니다.
위에서 제시했던 예제를 any -> unknown으로만 바꿔보겠습니다.
let unknownValue: unknown = '대충 문자열';
let stringValue: string = unknownValue;
위 코드는 오류가 발생합니다.
unknown은 어떤 타입에 할당이 불가능하니까요.
이제 오류가 생기지 않는 코드를 작성해보겠습니다.
let unknownValue: unknown = '대충 문자열';
let stringValue: string;
if (typeof unknownValue === 'string') stringValue = unknownValue;
typeof라는 type 가드로 unknown 타입의 값을 검사하는 코드가 추가됐습니다.
이렇게 하면 stringValue에 안전하게 값을 할당할 수 있습니다.
만약 unkownValue 값이 문자열이 아닌 값이라면 할당이 안 될겁니다.
2번째 예시에서도 마찬가지입니다.
let unknownValue: unknown = '대충 문자열';
if (typeof unknownValue === 'string') unknownValue.toUpperCase();
이렇게 unknown으로 한다면 런타임에서 오류를 잡아내지 않아도 됩니다.
🙂 내 사용 예시
import { saveAs } from 'file-saver'
import { toast } from 'react-toastify'
interface Parameter {
data: any
fileName: string
fileExtension: 'xlsx'
}
const fileTypes = {
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}
const excelDownload = ({ data, fileName, fileExtension }: Parameter) => {
if (!data) return toast.error('다운로드 버튼을 다시 눌러주세요.')
const fileBlob: Blob = new Blob([data], {
type: fileTypes[fileExtension],
})
saveAs(fileBlob, `${fileName}.${fileExtension}`)
}
export default excelDownload
위 코드는 제가 작성한 엑셀 다운로드 유틸 함수입니다.
data는@tanstack/react-query의 data를 받아오는데 작성 당시에는 무작정 만능 any 타입으로 처리 했었네요.
지금껏 보았던 오류와 다른 것 같지만 맥락은 같습니다.
위처럼 any 타입이면 data 타입이 Blob의 인자로 들어갈 수 없는 형태여도 들어가게 되는거죠.
참고) Blob의 첫 번째 인자는 BlobPart 형식의 []이어야 합니다.
그래서 data: any를 unknown으로 바꿔 보았습니다!
import { saveAs } from 'file-saver'
import { toast } from 'react-toastify'
interface Parameter {
data: unknown
fileName: string
fileExtension: 'xlsx'
}
const fileTypes = {
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}
const excelDownload = ({ data, fileName, fileExtension }: Parameter) => {
if (!data) return toast.error('다운로드 버튼을 다시 눌러주세요.')
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
const fileBlob: Blob = new Blob([data], {
type: fileTypes[fileExtension],
})
saveAs(fileBlob, `${fileName}.${fileExtension}`)
}
}
export default excelDownload
코드 줄 수가 늘어나긴 했지만 훨씬 안정적인 코드로 변했습니다.
📚 정리
any 대신 unknown을 사용해야 하는 이유?
1. any는 모든 걸 해결해주는 것 같지만 일반 JS와 다를 바가 없는 임시 대피처 같은 타입
2. any는 타입 검사를 거치지 않지만 unknown은 할당할 때 타입 검사를 요구함
3. any로 인한 런타임이나 언급하지 않았지만 빌드 타임의 오류를 방지할 수 있음
🔗 참고
- https://xionwcfm.tistory.com/394#%F0%9F%98%80%20unknown%EC%9D%80..-1
- perplexity AI
'Typescript' 카테고리의 다른 글
Type 지정 뭐 사용하시나요? (1) | 2024.03.27 |
---|---|
Typescript 왜 쓰는거지 🤔 (1) | 2024.02.11 |