Unknown 타입
any 와 동작이 흡사하여 주로 any 와 비교된다.
any 는 유형 검사를 우회한다.
- 반면,
unknown 은 값에 대한 연산을 수행하기 전에 명시적 assertion 혹은 narrowing 을 통해 타입 안정성을 유지하는 것이 가능하다.
unknown 은 타입스크립트 3.0 에서 도입되어, any 대신 사용했을 때 안정성이 더 뛰어나다.
any, unknown 타입에 다른 값을 할당하는 예시
- 두 타입으로 선언된 변수라면 어떤 primitive 타입의 값이든 할당이 가능하다.
let unknown: unknown;
unknown = "ab"; // string 할당
unknown = 123; // number 할당
let any: any;
any = "ab"; // string 할당
any = 123; // number 할당
- 서로가 서로의 타입에 할당하는 것도 가능하다.
// any 와 unknown 의 경우 서로 할당이 가능하다.
let any2: any = unknown;
let unknown2: unknown = any;
다른 타입의 변수에 unknown, any 를 할당하는 예시
- 다른 타입의 변수에 할당할 때는
any 는 마찬가지로 아무데나 할당이 가능하다.
- 단,
unknown 은 any, unknown 외에는 어떤 타입의 변수에도 할당이 불가능하다.
// unknown 은 any, unknown 외의 다른 타입의 변수에 할당이 불가능하다.
let arr: string[] = any; // 정상
let arr2: string[] = unknown; // 컴파일 타임 에러
메서드 이용 시 예시
any 타입의 변수에서는 어떤 메서드를 호출해도 컴파일 타임 에러가 나지 않는다.
- 마치 타입스크립트를 사용하지 않은 그냥 자바스크립트 변수 같다.
- 반대로
unknown 타입은 내로잉이 되기 전까지는 어떤 메서드를 호출해도 컴파일 타임 에러가 난다.
any 타입의 경우
let value: any;
// You can do anything with 'any', no errors are raised
value = 5; // OK
value = "hello"; // OK
value.someMethod(); // OK, no type checking
// No errors even if the property or method doesn't exist
value.nonExistentProperty; // OK
value.nonExistentMethod(); // OK
unknown 타입의 경우
let value: unknown;
// Assigning any value to 'unknown' is fine
value = 10; // OK
value = "world"; // OK
// But you cannot perform any operations without type checking
value.someMethod(); // Error: Object is of type 'unknown'
value.nonExistentProperty; // Error: Object is of type 'unknown'
// You need to use type assertions or type guards
if (typeof value === "string") {
console.log(value.toUpperCase()); // OK after type checking
}
type predicate 의 경우
- Type Predicate 의 경우,
any 와 unknown 둘 다 타입 유추가 가능하지만, 유추 전까지는 어떠한 연산도 불가능한 unknown 타입을 사용하는 것이 더 안정적이다.
// 사실 차이가 없다. 단, unknown 은 predicate 전까진 관련된 모든 작업이 에러로 표기되므로 unknown 을 사용하는 것이 더 안정적일 것이다.
if (isString(unknown)) {
unknown; // string 으로 타입 유추가 된다.
}
if (isString(any)) {
any; // string 타입으로 유추가 된다.
}
union 을 이용하는 경우
unknown 은 유니온 연산을 했을 때 primitive 타입과 유니온 되면, unknown 으로 추론이 된다.
- 단,
any 와 연산했을 때만 any 로 추론된다.
any 는 어떠한 타입과 유니온 연산을 해도 any 가 추론된다.
// 어떤 primitive 타입과 union 연산을 해도 unknown 타입이 된다.
type u1 = unknown | number; // unknown 으로 추론된다.
type u2 = unknown | string; // unknown 으로 추론된다.
type u3 = unknown | boolean; // unknown 으로 추론된다.
// 단, any 타입과 union 을 하는 경우에는 any 타입으로 추론된다.
type u4 = unknown | any; // any 로 추론된다.
// any 는 어떤 타입과 union 을 해도 any 로 추론된다.
type a1 = any | number; // any 로 추론된다.
type a2 = any | string; // any 로 추론된다.
type a3 = any | boolean; // any 로 추론된다.
intersection 을 이용하는 경우
any 는 union 과 마찬가지로 어떤 타입과 intersection 을 해도 any 로 추론된다.
unknown 은 primitive 타입과 intersection 을 하면 type narrowing 이 일어나 해당 타입이 된다.
- 단,
any 와 intersection 했을 때는 이전과 마찬가지로 any 로 추론이 된다.
// unknown 타입에 intersection 을 이용하면 narrowing 이 되어 상대 타입이 된다.
type ui1 = unknown & string; // string 으로 추론된다.
type ui2 = unknown & boolean; // boolean 으로 추론된다.
type ui3 = unknown & number; // number 로 추론된다.
type ui4 = unknown & any; // any 로 추론된다.
// any 타입은 intersection 을 이용해도 그대로 any 타입이 된다.
type ai1 = any & number; // any 로 추론된다.
type ai2 = any & string; // any 로 추론된다.
type ai3 = any & boolean; // any 로 추론된다.
연산 기호 사용
any 는 어떤 연산 기호든 다 받아들인다.
- 반면,
unknown 은 사칙연산 연산자 기호 사용이 불가능하다.
let an1: any = 10;
let an2: any = 10;
// any 는 모든 사칙연산 연산자가 가능하다.
an1 + an2;
an1 - an2;
an1 * an2;
an1 / an2;
// 동등 비교 연산자에 대해서도 정상이다.
an1 === an2;
an1 == an2;
an1 !== an2;
an1 != an2;
// unknown 은 모든 사칙연산 연산자에 대해 에러가 난다.
un1 + un2;
un1 - un2;
un1 * un2;
un1 / un2;
// 동등 비교 연산자에 대해서는 정상이다.
un1 === un2;
un1 == un2;
un1 !== un2;
un1 != un2;