#42 모르는 타입의 값에는 any 대신 unknown을 사용하기

2023. 3. 30.·🎨 프론트엔드 공부/JS & TS

이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리

📍요약

✅unknown은 any 대신 사용할 수 있는 안전한 타입이다

- 어떤 값이 있을 때, 그 타입을 알지 못하는 경우라면 unknown을 사용하면 된다

 

✅사용자가 타입 단언문이나 타입 체크를 사용하도록 강제하려면 unknown을 사용하면 된다

✅{}, object, unknown의 차이점을 이해해야 한다

 

📍함수의 반환값과 관련된 unknown

✅함수의 반환값으로 any 대신 unknown을 사용해야 한다

 

any를 사용하는 경우

function parseYAML(yaml: string): any {
  // ...
}
interface Book {
  name: string;
  author: string;
}
const book = parseYAML(`
  name: Jane Eyre
  author: Charlotte Brontë
`);
// book : 암시적 any 타입으로 사용하는 곳에서 에러 유발

alert(book.title);  // No error, alerts "undefined" at runtime
book('read');  // No error, throws "TypeError: book is not a
               // function" at runtime

 

- 타입에러로 스크린 불가하므로 바람직하지 않다

 

✅반환값을 unknown을 사용하는 경우

function parseYAML(yaml: string): any {
  // ...
}
interface Book {
  name: string;
  author: string;
}
// unknown 타입을 반환하는 함수로 래핑
function safeParseYAML(yaml: string): unknown {
  return parseYAML(yaml);
}
const book = safeParseYAML(`
  name: The Tenant of Wildfell Hall
  author: Anne Brontë
`);
alert(book.title);
   // ~~~~ Object is of type 'unknown'
book("read");
// ~~~~~~~~~~ Object is of type 'unknown'

 

타입에러를 유발할 수 있다

 

📍할당 가능성

- 어떠한 타입이든 any 타입에 할당 가능

- any 타입은 어떠한 타입으로도 할당 가능 (never 제외)

declare function temp1(): never;
declare function temp2(): any;

// 어떠한 타입이든 any 타입에 할당 가능하다
const a: any = temp1(); // never 타입을 any 타입에 할당

// any 타입은 어떠한 타입으로도 할당 가능하다 (never 제외)
const b: never = temp2(); // 타입 에러 : any를 never에 할당 불가
const c: string = temp2(); // OK

 

⭐한 집합은 다른 모든 집합의 부분 집합이면서 동시에 상위 집합이 될 수 없다

- any는 이 법칙을 무시하기 때문에 강력하면서 에러를 일으킨다

 

✅반면 unknown 타입은

- 어떠한 타입이든 unknown 타입에 할당 가능

- unknown 타입은 어떠한 타입으로도 할당 가능하진 않다 (unknown 과 any 에만 할당 가능)

 

any의 1번은 같지만, 2번은 다름

 

✅반면 never 타입은 unknown과 정반대

- 어떠한 타입도 never에는 할당 불가

- never 타입은 어떠한 타입으로도 할당 가능

 

- 예제

declare function fnNever(): never;
declare function fnAny(): any;
declare function fnUnknown(): unknown;
declare function fnString(): string;

const a: any = fnNever(); // never를 any에 할당 (가능)

const b: never = fnAny(); // any를 never에 할당 (불가)
const c: string = fnAny(); // any를 string(모든 타입)에 할당 (가능)

const d: number = fnUnknown(); // unknown을 number에 할당 (불가)

const e: unknown = fnString(); // string(모든 타입)을 unknown에 할당 (가능)

 

 

⭐그래서 맨 처음의 예제에서 함수의 반환 타입이 unknown 이어도 Book 타입 단언 가능

(unknown 상태로 그대로 사용 불가)

function parseYAML(yaml: string): any {
  // ...
}
interface Book {
  name: string;
  author: string;
}
function safeParseYAML(yaml: string): unknown {
  return parseYAML(yaml);
}
const book = safeParseYAML(`
  name: Villette
  author: Charlotte Brontë
`) as Book; // unknown 타입에 어떤 타입이든(Book) 할당 가능

alert(book.title);
        // ~~~~~ Property 'title' does not exist on type 'Book'
book('read');
// ~~~~~~~~~ this expression is not callable

 

📍변수 선언과 관련된 unknown

✅어떠한 값이 있지만 그 타입을 모를 때 unknown을 사용

- 그래서 일단 함수의 매개변수에 unknown을 부여하고 타입체크 테크닉을 이용..

interface Book {
  name: string;
  author: string;
}
interface Geometry {}
interface Feature {
  id?: string | number;
  geometry: Geometry;
  properties: unknown;
}
function processValue1(val: unknown) {
  // instanceof 연산자로 Date 타입 걸러내기
  if (val instanceof Date) {
    val  // Type is Date
  }
}

// 타입 가드
function isBook(val: unknown): val is Book {
  return (
      typeof(val) === 'object' && val !== null &&
      'name' in val && 'author' in val
  );
}
function processValue2(val: unknown) {
  if (isBook(val)) {
    val;  // Type is Book
  }
}

 

📍단언문과 관련된 unknown

✅이중 단언문

- 아래의 이중 단언문은 최종적으로 Bar 타입으로 결정되는 것에서 기능적 차이는 없지만,

중간 과정에서 any 보다는 unknown이 예기치 못한 악영향을 막을 수 있다

interface Foo { foo: string }
interface Bar { bar: string }

declare const foo: Foo;

// 타입을 any -> Bar 변환 : any 로 바꾸면 다른 곳에서 에러 발생
let barAny = foo as any as Bar; // 최종 타입 bar

// unknown을 거치는 것이 바람직함
let barUnk = foo as unknown as Bar; // 최종 타입 Bar

 

✅ {} 타입

null과 undefined를 제외한 모든 값 포함

let temp1: {};
temp1 = 23;

let temp2: {};
temp2 = null; // 타입에러 : null 을 {} 에 할당 불가

let temp3: {};
temp3 = undefined; // 타입에러 : undefined 를 {} 에 할당 불가

 

✅object 타입

모든 비원시값 (객체나 배열)

let a: object;

// 가능
a = [];
a = {};
a = () => {};

// 불가
a = 23;
a = "abc";
c = false;

 

⭐unknown 타입이 도입되면서 {} 대신 unknown을 더 많이 쓰는 추세

null / undefined 가 불가능한 것이 확실하면 {}를 사용하면 됨

'🎨 프론트엔드 공부/JS & TS' 카테고리의 다른 글
  • #43 몽키 패치보다는 안전한 타입을 사용하기
  • JavaScript 실행시간 측정 : performance.now()
  • #41 any의 진화를 이해하기
  • #40 함수 안으로 타입 단언문 감추기
지식물원
지식물원
지식이 자라는 식물원!
  • 지식물원
    지식물원
    지식물원
  • 전체
    오늘
    어제
    • 분류 전체보기 (516)
      • 🎨 프론트엔드 공부 (253)
        • JS & TS (92)
        • HTML & CSS (22)
        • React & Next (49)
        • Vue & Nuxt (22)
        • 기타 (68)
      • 🤓 기술 학습 & 공부 기록 (116)
        • Node.js (0)
        • Python (37)
        • 백엔드 (0)
        • 딥러닝 (1)
        • 컴퓨터 일반 (72)
        • 개발 인프라 (6)
      • 👨‍💻 프로젝트 경험 (6)
        • Work (0)
        • Toy (6)
      • ⚙️ 개발 팁 & 노하우 (21)
        • 프론트엔드 (6)
        • 기타 (15)
      • ☕️ 커리어 & 인터뷰 준비 (88)
        • 코딩 테스트 (88)
      • 📰 기술 트렌드 & 생각 정리 (4)
      • 📚 기타 (25)
        • 마케팅 (15)
        • 비개발서적 (10)
  • 블로그 메뉴

    • 태그
  • 링크

  • 공지사항

    • 모바일 접속 시 코드 하이라이팅 깨질 때
  • 인기 글

  • hELLO· Designed By정상우.v4.10.3
지식물원
#42 모르는 타입의 값에는 any 대신 unknown을 사용하기
상단으로

티스토리툴바