[TIL # 44] TypeScript (2)

Yejin Yang·2022년 6월 23일
0

[TIL]

목록 보기
43/69
post-thumbnail

타입 추론(Inference)

타입스크립트가 코드를 해석해 나가는 동작을 의미

타입 추론의 기본

let x = 3;

x에 대한 타입을 따로 지정하지 않더라도 일단 x는 number로 간주하는 것을 타입추론이라고 한다.

타입추론이 일어나는 경우

  • 초기화된 변수
  • 기본값이 설정된 매개 변수
  • 반환 값이 있는 함수

타입 단언(Assertions)

타입스크립트가 타입 추론을 통해 판단할 수 있는 타입의 범주를 넘는 경우, 더 이상 추론하지 않도록 지시할 수 있. 프로그래머가 원하는 임의의 타입을 값에 할당하고 싶을 때, 프로그래머가 타입스크립트보다 타입에 대해 더 잘 이해하고 있는 상황일 때 필요한 것이 바로 타입 단언이다.

// 예제 
function someFunc(val: string | number, isNumber: boolean) {
  // some logics
  if (isNumber) {
    val.toFixed(2); // Error - TS2339: ... Property 'toFixed' does not exist on type 'string'.
  }
}

toFixed() 메서드는 숫자를 고정 소수점 표기법으로 표시하는 숫자 Number prototype 메소드이다.

예제 해석

  • 함수의 매개 변수 val은 유니언 타입으로 문자열(String)이거나 숫자(Number)일 수 있는 상황
  • 매개 변수 isNumber는 불린(Boolean)이며, 우리는 이름을 통해 숫자 여부를 확인하는 값이라는 것을 추론할 수 있다.
  • 따라서 우리는 isNumbertrue일 경우 val은 숫자일 것이고, 이에 toFixed를 사용할 수 있음을 알 수 있다.

하지만 타입스크립트는 isNumber라는 이름만으로 위 내용을 추론할 수 없기 때문에 “val이 문자열인 경우 toFixed를 사용할 수 없다”고 (컴파일 단계에서) 에러를 반환 한 것이다.

타입 단언하기

따라서 isNumbertrue일 때 val이 숫자임을 단언할 수 있다.

function someFunc(val: string | number, isNumber: boolean) {
  // some logics
  if (isNumber) {
    // 1. 변수 as 타입
    (val as number).toFixed(2);
    // Or
    // 2. <타입>변수
    // (<number>val).toFixed(2);
  }
}
  1. 변수 as 타입
  2. <타입>변수
    두 번째 방식(<number>val)은 JSX를 사용하는 경우 특정 구문 파싱에서 문제가 발생할 수 있으며, 결과적으로 .tsx 파일에서는 전혀 사용할 수 없다.

Non-null 단언 연산자

!를 사용하는 Non-null 단언 연산자를 통해 피연산자가 Nullish(null이나 undefined) 값이 아님을 단언할 수 있는데, 변수나 속성에서 간단하게 사용할 수 있기 때문에 유용하다.

// Error - TS2533: Object is possibly 'null' or 'undefined'.
function fnA(x: number | null | undefined) {
  return x.toFixed(2);
}

매개 변수 x는 함수 내에서 toFixed를 사용하는 숫자 타입으로 처리되지만 null이나 undefined일 수 있기 때문에 에러가 발생한다.

// Non-null assertion operator
function fnE(x: number | null | undefined) {
  return x!.toFixed(2);
}

!를 사용하는 Non-null 단언 연산자를 이용해 간단하게 Nullish(null이나 undefined) 값이 아님을 단언할 수 있다.


타입 가드(Guards)

타입을 매번 보장하기 위해 타입 단언을 여러 번 사용하게 되는 경우가 있다.
타입 가드를 제공하면 타입스크립트가 추론 가능한 특정 범위(scope)에서 타입을 보장할 수 있다.

사용자 정의 타입 가드

사용자 정의 타입 가드는 value is Type 형태의 반환 타입을 갖는 함수로 정의한다.

// 타입 가드
function isNumber(val: string | number): val is number {
  return typeof val === 'number';
}

function someFunc(val: string | number) {
  if (isNumber(val)) {
    val.toFixed(2);
    isNaN(val);
  } else {
    val.split('');
    val.toUpperCase();
    val.length;
  }
}

위의 예제는 val is number 으로 타입 단언을 해준 것이다.

위 방식뿐만 아니라 제공 가능한 타입 가드들
: typeof, in , instanceof
비교적 단순한 로직에서 추천되는 방식

typeof 연산자는 number, string, boolean, 그리고 symbol만 타입 가드로 인식할 수 있다.

in 연산자의 우변 객체(val)는 any 타입이어야 한다.


인터페이스(interface)

인터페이스(Interface)는 타입스크립트 여러 객체를 정의하는 일종의 규칙이며 구조이다.
interface 키워드와 함께 사용한다.


interface IUser {
  name: string,
  age: number,
  isAdult: boolean
}

let user1: IUser = {
  name: 'Neo',
  age: 123,
  isAdult: true
};

// Error - TS2741: Property 'isAdult' is missing in type '{ name: string; age: number; }' but required in type 'IUser'.
let user2: IUser = {
  name: 'Evan',
  age: 456
};

user2는 isAdult를 사용하지 않았기 때문에 에러가 발생한다. 속성에 ?를 사용하면 선택적 속성으로 정의할 수 있다.

interface IUser {
  name: string,
  age: number,
  isAdult?: boolean // Optional property
}

// `isAdult`를 초기화하지 않아도 에러가 발생하지 않습니다.
let user: IUser = {
  name: 'Neo',
  age: 123
};
  

읽기 전용 속성(Readonly properties)

readonly 키워드를 사용하면 초기화된 값을 유지해야 하는 읽기 전용 속성을 정의할 수 있다.

interface IUser {
  readonly name: string,
  age: number
}

// 초기화
let user: IUser = {
  name: 'Neo',
  age: 36
};

user.age = 85; // Ok
user.name = 'Evan'; // Error - TS2540: Cannot assign to 'name' because it is a read-only property.

만약 모든 속성이 readonly일 경우, 유틸리티(Utility)나 단언(Assertion) 타입을 활용할 수 있다.

// All readonly properties
interface IUser {
  readonly name: string,
  readonly age: number
}

// Readonly Utility
interface IUser {
  name: string,
  age: number
}
let user: Readonly<IUser> = {
  name: 'Neo',
  age: 36
};
user.age = 85; // Error
user.name = 'Evan'; // Error


// Type assertion
let user = {
  name: 'Neo',
  age: 36
} as const;
user.age = 85; // Error
user.name = 'Evan'; // Error

//  as const: let 변수의 경우에도 const처럼 literal type으로 추론해 줄 수 있는데, 그 때 사용하는 것이 as const 이다.
profile
Frontend developer

0개의 댓글