[typescript] unknow 타입

Yeonjoo Yoo·2022년 1월 26일
0

TIL

목록 보기
3/12
ts 공부를 시작한지 얼마 안 되어서, js파일을 ts로 점차 수정하는데 소스에 any가 남발하고 있는 중이다.
이런 코드라면 ts를 쓰는 이유가 없는 것 같다 생각했다.
더욱 ts 답게 고칠 수 있는 방법을 찾던 중 unknow 타입에 대해 접하게 되었다.

unknow 타입

  • TypeScript 3.0 부터 도입된 타입
  • 'any' 타입과 대응되는 type-safe한 타입

특징

  • 모든 타입의 값이 unknow에 할당 가능 (any와 동일)
    하지만, unknow을 unknow과 any을 제외한 타입에 할당 하는 것은 불가능
// Anything is assignable to unknown
function f21<T>(pAny: any, pNever: never, pT: T) {
  let x: unknown;
  x = 123;
  x = "hello";
  x = [1, 2, 3];
  x = new Error();
  x = x;
  x = pAny;
  x = pNever;
  x = pT;
}
// unknown assignable only to itself and any
function f22(x: unknown) {
  let v1: any = x;
  let v2: unknown = x;
  let v3: object = x; // Error
  let v4: string = x; // Error
  let v5: string[] = x; // Error
  let v6: {} = x; // Error
  let v7: {} | null | undefined = x; // Error
}
  • unknown과 다른 타입을 intersection (&) 하면 대상 타입을 반환
    반대로, unknown과 다른 타입을 union (|) 하면 unknown 타입을 반환
    단, 'unknow | any' 은 any 반환
// 1. intersection
type T00 = unknown & null; // null
type T01 = unknown & undefined; // undefined
type T02 = unknown & null & undefined; // null & undefined (which becomes never)
type T03 = unknown & string; // string
type T04 = unknown & string[]; // string[]
type T05 = unknown & unknown; // unknown
type T06 = unknown & any; // any

// 2. union
type T10 = unknown | null; // unknown
type T11 = unknown | undefined; // unknown
type T12 = unknown | null | undefined; // unknown
type T13 = unknown | string; // unknown
type T14 = unknown | string[]; // unknown
type T15 = unknown | unknown; // unknown
type T16 = unknown | any; // any
  • 조건부 삼항 연산자
type T30<T> = unknown extends T ? true : false; // Deferred
type T31<T> = T extends unknown ? true : false; // Deferred (so it distributes)
type T32<T> = never extends T ? true : false; // true
type T33<T> = T extends never ? true : false; // Deferred
  • keyof unknown 은 'never'
    • never : 절대로 발생하지 않는 값의 타입
      함수의 리턴 타입으로 never 가 사용될 경우, 항상 오류를 출력하거나 리턴 값을 절대로 내보내지 않음을 의미
type T40 = keyof any; // string | number | symbol
type T41 = keyof unknown; // never
  • 오직 등위 연산자만 unknown과 사용 가능
// 
function f10(x: unknown) {
  x == 5;
  x !== 10;
  x >= 0; // Error
  x + 1; // Error
  x * 2; // Error
  -x; // Error
  +x; // Error
}
  • 프로퍼티에 접근할 수 없으며, 메소드를 호출할 수 없으며, 인스턴스를 생성할 수 없음
    Type Gaurd 사용하면 가능
function f11(x: unknown) {
  x.foo; // Error
  x[5]; // Error
  x(); // Error
  new x(); // Error
}

// typeof, instanceof, and user defined type predicates
declare function isFunction(x: unknown): x is Function;

function f20(x: unknown) {
  if (typeof x === "string" || typeof x === "number") {
    x; // string | number
  }
  if (x instanceof Error) {
    x; // Error
  }
  if (isFunction(x)) {
    x; // Function
  }
}
  • Homomorphic mapped type over unknown
type T50<T> = { [P in keyof T]: number };
type T51 = T50<any>; // { [x: string]: number }
type T52 = T50<unknown>; // {}
  • Spread types 은 객체에서만 사용 가능
function f26(x: {}, y: unknown, z: any) {
  let o1 = { a: 42, ...x }; // { a: number }
  let o2 = { a: 42, ...x, ...y, ...z }; // Error at '...y'
}
  • 'void'나 'any'를 제외한 타입이 지정되면 return 값이 있어야 함
function f27(): unknown {} // Error. 반환값 필요

any 타입과 비교

  • unknown 타입은 any 타입이 쓰이는 모든 곳에 사용 가능
    즉, any 타입 대체 가능
  • any 타입 보다 안전하게 코드 작성 가능
const x: any = {
  a: "a-value",
  b: "b-value"
};
x.a; // OK
x.c = "c-value"; // OK

const y: unknown = {
  a: "a-value",
  b: "b-value"
};
y.a; // ERROR
(y as {a: string; b: string; }).a; // OK
y.c = "c-value"; // ERROR

정리

어떤 타입이 올지 모르는 상황에서 더욱 type-safe 하도록 작성하려는 경우에 unknown 타입을 활용하기

참고
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type
https://mainawycliffe.dev/blog/typescript-use-unknown-instead-of-any/

profile
to be frontend engineer

0개의 댓글