타입스크립트의 타입 계층 구조이다.
자바스크립트에 존재하는 모든 값을 오류 없이 받을 수 있다.
즉, 타입을 명시하지 않은 것과 동일하다.
let state: any;
state = { value: 0 }; // 객체를 할당해도
state = 100; // 숫자를 할당해도
state = "hello world"; // 문자열을 할당해도
state.foo.bar = () => console.log("this is any type");
// 심지어 중첩 구조로 들어가 함수를 할당해도 문제없다
당연히 이 any 타입은, 지양해야 할 패턴으로 알려져있다. 그러나, 아래 3가지의 경우 사용을 고려해보자.
개발 과정에서 추후 값이 변경될 가능성이 있거나, 타입이 확정되지 않은 경우
API 요청 및 응답 처리, 콜백함수 전달, 정제되지 않은 외부 라이브러리 등 특정하기 힘든 경우
type FeedbackModalParams = {
show: boolean;
content: string;
cancelButtonText?: string;
confirmButtonText?: string;
beforeOnClose?: () => void;
action?: any;
};
외부 라이브러리나 웹 API의 요청에 따라 다양한 값을 반환하는 API가 존재할 수 있다.
async function load() {
const response = await fetch("https://api.com");
const data = await response.json();
// response.json()의 리턴 타입은 Promise<any>로 정의되어 있다
return data;
}
unknown
: 무엇이 할당될지 아직 모르는 상태
let unknownValue: unknown;
unknownValue = 100; // any 타입과 유사하게 숫자이든
unknownValue = "hello world"; // 문자열이든
unknownValue = () => console.log("this is any type"); // 함수이든상관없이할당이가능하지만
let someValue1: any = unknownValue; // (O) any 타입으로 선언된 변수를 제외한 다른 변수는 모두 할당이 불가
let someValue2: number = unknownValue; // (X)
let someValue3: string = unknownValue; // (X)
any
: 어떤 타입이든 any 타입에 할당 가능하며 어떤 타입으로도 할당 가능unknown
: 어떤 타입이든 unknown 타입에 할당 가능하며 any를 제외한 다른 타입으로 할당 불가능위 예제에서 흥미로운건, 함수를 unknown타입 변수에 할당할때는 컴파일러가 경고를 주지 않지만, 실행하면 아래와 같은 에러가 발생한다.
// 할당하는 시점에서는 에러가 발생하지 않음
const unknownFunction: unknown = () => console.log("this is unknown type");
// 하지만 실행 시에는 에러가 발생; Error: Object is of type 'unknown'.ts (2571)
unknownFunction();
함수 뿐만 아니라 객체의 속성 접근, 클래스 생성자 호출을 통한 인스턴스 생성 등 객체 내부에 접근하는 모든 시도에서 에러가 발생한다.
unknown 타입은 어떤 타입이 할당되었는지 알 수 없음을 나타내는 역할이기에, 어떤 값이든 올 수 있음과 동시에 개발자에게 엄격한 타입 검사를 강제하는 의도를 담고있다.
any를 사용하면 어떤 값이든 허용된다. 그러나 특정타입으로 수정하지 않고 누락하면 예상치못한 버그가 발생할 수 있다.
unknown은 이러한 상황을 보완하기 위해 등장한 타입이다. any와 유사하지만 타입 검사를 강제하고, 식별된 후에 사용할 수 있기에 any보다 안전하다.
- any는 아무때나 쓸 수 있지만, typescript를 쓰는 의미가 없게 되어버릴 수 있다.
- unknown을 어쩔 수 없이 써야할 상황이 있을 수도 있으나, 이를 사용하는 쪽에서의 방어처리를 해서 안전하게 사용가능하다.
위와 같은 이유로, 궁극적으로 우리는 any를 사용하지 않기로 정했고, 피치 못하게 사용해야 할 상황이나, 꼭 필요한 상황에는 unknown을 사용하기로 정하였습니다. 그렇게 해서, 사용하는 쪽에서 한 번의 처리를 더 강제하고, 안전하게 사용을 유도하는 것이죠.
https://techblog.woowahan.com/9804/#toc-2
냥이팀 : any는 웬만하면 쓰지 말자고 얘기하고 있어요. 특히 런타임에 오류를 방지하고 싶다면 any를 쓰지 말아야죠. 하지만 특정 타입을 어떻게 좁혀서 사용할지 모를 때 어쩔 수 없이 any를 사용한 적이 있어요. 응답객체의 구조를 정확히 몰랐죠. 하지만 지금 생각해보니 any를 쓸 필요가 없는 것 같아서 리팩터링하며 전부 고치고 있어요.
타입스크립트 베이스로 데이터 플로우가 다 들어있다면 any가 필요한 상황이 거의 없겠죠. 그러나 Axios 라이브러리의 응답 객체 기본값이 any인걸 보면 필요한 상황이 있을 것 같긴해요
그러나 깨진 유리창 이론처럼, any를 한번 사용하면 남발하게 될까 경계하고 있습니다.
감자팀 : 웬만하면 any를 지양하려 하고 있어요. 하지만 아예 안쓰기엔 어려운 것 같아요. 표 컴포넌트같이 어떤 값을 받을 지 모르는 상황에서 unknown을 사용하면, 가공할때 타입 캐스팅을 전부 해야해서 이럴 때 any를 사용하고 있습니다.
배달이팀 : 강제 타입 캐스팅을 통해 타입을 전환할 때 사용합니다.
const env = process.env as unknown as ProcessEnv
같은 식으로요.
냥이팀 : any보다는 좀 더 많이 사용하는 것 같아요. any는 무엇이든 괜찮다. unknown은 뭔지 모르지만 하나씩 테스트하며 알아내보자 라는 의미라고 생각하는데, 후자의 논리전개가 필요하다면 unknown을 사용합니다. 또 unknown으로 선언되면 any와 달리 에러가 발생해서 더 엄격해서 unknown이 마음에 듭니다.
왕팀 : 예상할 수 없는 데이터라면 unknown을 사용합니다. 타입스크립트 4.4부터 try - catch에러의 타입이 any에서 unknown으로 변경되어 에러 핸들링할 때도 unknown을 사용합니다. 또 as unknown as Type같이 강제 타입 캐스팅을 하기도 하는데 사실 이것도 any랑 다를게 없어서 지양해야합니다.