치춘짱베리굿나이스
any, unknown, never 본문
any, unknown, never
any
와 unknown
둘이 상당히 비슷해 보이는데 살짝 다른, 특별한 타입 키워드이다
never
는 갑자기 생각나서 추가했다
any
let a: any = 1;
any
쓰면 애니추천
모든 타입이 할당될 수 있는 타입이다
메타몽 같은 타입이라고 생각하면 된다… any
는 무엇이든 될 수 있다
any
를 쓴다는 것은 사실상 “타입 체크를 하지 마시오” 라고 말하는 것과 같다
특징
let a: any = 1;
a = [1, 2, 3];
a = "hello";
a = { name: "abc" }
모든 타입이 할당될 수 있다는 것은, 위처럼 모든 타입의 값들을 할당받을 수 있다는 뜻이다
any
는 무엇이든 될 수 있기 때문에! 위처럼 어떠한 값을 대입하든 오류가 발생하지 않는다
let a: any = "hello";
let b: number = a;
any
는 어떠한 타입이든 될 수 있기 때문에! 어떠한 타입의 변수에도 할당 가능하다
위와 같은 경우에도 정상적으로 할당이 가능하다
여담으로 위의 케이스는 당연히 컴파일도 안 되거나 NaN
으로 처리될 줄 알았는데 b
가 number
가 아닌 string
으로서 동작을 하더라…
let a: any = 1;
console.log(a + 1);
console.log(a.toString());
console.log(a.map((v: number) => v + 1));
다른 타입 객체에 존재하는 메서드 등을 마구잡이로 호출해도 정적 타입 검사에선 걸리지 않는다 (빨간 줄이 없는 것을 볼 수 있음)
any
는 무엇이든~ 될 수 있기 때문에 모든 타입 검사를 만족한다
물론 실행시키면 해당 메서드가 실제로 존재하지 않기 때문에 터진다
console.log(a.unavailableWhateverMethod());
어느 타입에도 존재하지 않는 메서드를 아무렇게나 호출해도 정적 타입 검사에서 걸리지 않는다
세상에 타입이 얼마나 많은데 하나 쯤은 위와 같은 메서드를 가지고 있을 수도 있는 것 아닐까?
any
는 어떠한 타입이든 될 수 있기 때문에 위와 같은 메서드도 가질 수 있는 타입으로 처리되는 것이다
let c;
선언만 하고 초기화가 없는 변수는 기본적으로 암묵적 any
처리된다
이게 싫다면 (아예 타입 검사 단계에서 오류로 처리하고 싶다면) tsconfig
에서 noImplicitAny
속성을 true
로 바꿔두면 된다
사용처
타입스크립트의 타입 시스템을 일시적으로 우회하는 데에 많이 사용한다
자바스크립트로 작성된 코드를 타입스크립트로 순차적으로 마이그레이션 할 때, 우선 any를 사용해서 코드가 돌아가게끔 수정해둔 뒤 나중에 제대로 된 타입을 추가하는 식이다
단점
any
는 모든 타입이 될 수 있기 때문에 위처럼 타입 검사에 걸리지 않으며, 이는 타입스크립트를 쓰는 가장 큰 이유를… 회피하는 것과 같다
any
의 “모든 타입이 될 수 있다” 라는 부분은 의도치 않은 형변환과 위처럼 number
타입 변수에 string
이 대입되는 등의 부작용을 일으킬 수 있으므로 프로그램의 안정성이 떨어지게 된다
또한 any
는 한번 쓰면 다른 코드에서도 any
를 써야 하는 경우가 점점 많아지는 등 코드 내부에서 퍼져나가기 때문에 더더욱 조심해야 한다
unknown
let a: unknown = 1;
any
와 비슷하게 모든 타입이 될 수 있는 타입이다
any
와 비슷하지만, 타입 추론을 아예 꺼 버리는 any
와 다르게 좀 더 명시적으로 “나 여기 들어가야 할 타입이 뭔지 모르겠다” 라는 뜻을 가진다
unknown
을 발견한다면 “여기 들어가야 하는 타입의 종류가 뭔지 모르겠으니 누가 좀 도와줘" 라고 생각하자
타입스크립트 문서에서의 unknown
TypeScript 3.0 introduces a new top type unknown. unknown is the type-safe counterpart of any. Anything is assignable to unknown, but unknown isn’t assignable to anything but itself and any without a type assertion or a control flow based narrowing. Likewise, no operations are permitted on an unknown without first asserting or narrowing to a more specific type.
unknown
은 타입스크립트 3.0에 추가된 최상위 타입으로, any
의 타입-안전한 (type-safe) 대응책이라고 한다
어떠한 것이든 unknown
에 할당할 수 있지만, unknown
는 타입 단언 없이는 unknown
과 any
를 제외한 다른 타입에 할당할 수 없다
마찬가지로 첫 타입 단언 또는 다른 구체적인 타입으로 좁혀주지 않으면 unknown
변수에 다른 어떠한 연산도 불가능하다
특징
let a: unknown = 1;
a = [1, 2, 3];
a = "hello";
a = { name: "abc" }
unknown
은 any
와 마찬가지로 어떠한 타입이든 할당받을 수 있다
unknown
또한 위처럼 어떠한 값이 들어오더라도 오류가 발생하지 않는다
let a: unknown = "hello";
let b: number = a;
다만 여기서 any
와의 차이점이 드러난다
unknown
은 어떠한 타입이든 될 수는 있지만, 타입이 정해진 다른 변수에 할당이 불가능하다
let a: unknown = "hello";
let b: number = a as number;
unknown
타입은 명시적으로 타입을 변환해주지 않는 이상 다른 타입에 할당할 수 없다
반대로 말하면, 위처럼 명시적으로 다른 타입으로 변환해줬다면 정상적으로 할당이 가능하다
let a: unknown = "hello";
console.log(a + 1);
console.log(a.toUpperCase());
console.log(a[0]);
unknown
은 any
와 다르게 첫 타입 단언 없이는 어떠한 조작도 할 수 없다
모든 메서드들이 정적 타입 검사에 걸리지 않던 any
와 달리, unknown
은 어떠한 메서드나 연산을 실행시키든 타입 검사 시에 막혀버린다
unknown
은 모든 타입이 가지고 있는 공통적인 연산밖에 수행할 수 없는데, 이게 문제는 사칙연산도 공통 연산에 포함이 안 된다는 것…
혹시 “모든 타입이 수행할 수 있는 공통적인 연산” 의 예시를 아시는 분은 제보 부탁드립니다,,
let a: unknown = "hello";
console.log((a as number) + 1);
console.log((a as string).toString());
console.log((a as Array<number>)[0]);
따라서 unknown
타입은 다른 타입으로 좁혀서 사용해줄 의무가 있다
위처럼 아무렇게나 타입을 명시해 주면 그에 해당하는 메서드 또는 연산을 사용할 수 있는 것을 볼 수 있다
장점
unknown
의 “타입을 좁혀주지 않으면 연산이 불가한” 특성 때문에 어떠한 타입의 메서드든 마구 실행시켜 버리는 (그러고 오류가 나 버리는) any
보다 조금 더 안전하다
any
일 때 타입 체크를 그냥 스킵해버리는 이슈를 보완한 것과 마찬가지 효과를 내며, 타입 단언문이나 타입 체크를 필수적으로 수행함으로써 더 안전한 프로그램을 작성할 수 있다
never
function thisFunctionOnlyThrowsError(): never {
throw new Error("Error!");
}
사실 never
는 위의 두 타입과는 관계가 딱히 없다
일반적으로 함수의 반환값 타입으로 사용되며, “이 함수는 값을 반환할 리가 없다” 라는 의미를 내포한다
무조건 오류를 던지는 함수이거나, 무한 루프 함수의 경우 never
타입을 사용한다 (또는 그렇게 추론된다)
never
에 대한 더욱 자세한 내용은 이 글을 읽어보자
특징
let a: never = 1;
never
타입은 어떠한 타입도 될 수 없다
let b: string & number = 1;
타입 집합이 잘못되었을 경우 (호환되지 않는 타입간의 교차 타입 등) 에도 never
로 대체된다
never
는 그 자체로 “불가능함” 을 뜻한다고 볼 수 있다
function foo(): never {
throw new Error("");
}
let a = foo();
let b: string = a;
let c: number = a;
let d: boolean = a;
let e: any = a;
never
타입은 모든 타입의 하위 타입이기 때문에, unknown
과 반대로
- 어떠한 타입도
never
변수에 할당할 수 없지만 - 모든 타입에 할당할 수 있다
참고 자료
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html
https://www.dgmunit1.com/blog/typescript/item38_44
https://stackoverflow.com/questions/51439843/unknown-vs-any
https://jbee.io/typescript/TS-9-unknown/
'Javascript + Typescript > 이론과 문법' 카테고리의 다른 글
호이스팅 (0) | 2023.08.24 |
---|---|
실행 컨텍스트 (0) | 2023.08.23 |
var, let, const 차이점 (0) | 2023.08.17 |
문자열의 특정 문자 변경하기 (0) | 2023.08.08 |
2차원 배열에서 값 단 하나만 바꾸고 싶은데 모든 줄이 다 바뀌는 경우 (0) | 2023.08.08 |