TypeScript - Type Guard, Type Compatibility

박정호·2022년 10월 26일
0

TypeScript

목록 보기
6/8
post-thumbnail

⭐️ 타입 가드

타입가드를 사용하면 조건문에서 객체의 타입을 좁혀(type narrow)나갈 수 있고, 특정 범위 안에서 런타임 타입 검사를 수행하여 하나의 특정 타입을 갖도록 하나의 특정 타입으로 제한하는 표현식이다.

✏️ Example

아래와 같이 다른 타입의 속성들을 가지는 두개의 인터페이스를 유니온타입으로 정의했다가 하자. 이럴 경우 타입스크립트에서는 상세한 추론을 하지 못한다.

즉, 어느 인터페이스의 속성을 사용할지 모르기 때문에 확실하게 보장되어있는 공통 속성인 name만 사용할 수 있다고 판단한다.

따라서, introduce()의 반환값을 가지는 result가 skill 또는 age를 사용하려하면 Error가 발생한다.

👎 타입 단언을 통한 해결

이와 같은 문제를 타입단언을 통해서 해결할 수 있다.

as를 사용하여 result에 Developer, Person 타입을 단언해주는 것이다.
그리고 if문을 통한 검증을 통해서 skill, age 속성을 사용할 수 있게 된다.

✅ 단, 중복적인 코드가 많고 가독성이 매우 떨어진다는 단점이 존재한다. 그래서 이를 타입가드를 통해 해결할 수 있다.

💡 타입단언이란? 👉 Type Inference, Type Assertion

👍 타입 가드을 통한 해결

위의 경우는 is(사용자 정의 타입가드)를 통해 해결하였다.

1️⃣ is

is의 사용은 사용자 정의 타입가드라고 할 수 있다.

is(사용자 정의 타입가드)는 TS 가 타입을 판단하는 방법을 직접 정의하거나, 타입을 판단하는 로직을 재사용하고 싶을 때 사용하면 된다.

  • target is Developer(__는 __다(타입명제))넘겨받은 파라미터가 실제 해당 타입인지 구분하는 키워드라고 할 수 있다.

  • return (target as Developer).skill !== undefinedtarget이라는 obj에 skill이 있을때 Developer 타입이라고 취급하겠다는 것

  • Devleper를 취급하였듯이 person으로 코드를 구현하여도 가능하다.

  • 코드의 간결성과 가독성이 높아졌다.

2️⃣ typeof

가장 기본적인 type narrow(타입 축소) 방법이다. 기본적인 값에 대해서만 반환하여 타입 축소를 하기 때문에 우리가 원하는 커스텀 타입(ex. type, interface)을 비교할 수 없다는 단점이 있다.

💡 잠깐) JS와의 호환을 위해 typeof로 타입 가드할 수 있는 타입은 JS에서 제공하는 타입(타입스크립트 내장 타입)만 가능하다.

'string' / 'number' / 'bigint' / 'boolean' / 'symbol' / 'undefined' / 'object' / 'function'

3️⃣ in

in은 타입이 일치한지를 판단하는 typeof와 달리 객체 내부에 특정 property(속성)가 존재하는지를 확인하는 연산자이다. 따라서, 더 섬세하게 타입가드가 가능하다.

4️⃣ instance of

typeof와 유사하다. 하지만, class와 같은 커스텀타입에서 타입축소를 진행할 때의 타입가드 방법이다.

⭐️ 타입 호환

타입 호환이란 타입스크립트 코드에서 특정 타입이 다른 타입에 잘 맞는지를 의미한다.

즉, 특정 타입 간에 구조가 비슷하면 타입 호환이 될 수 있다는 뜻이다.

1️⃣ 구조적 타이핑

구조적 타이핑이란 코드 구조 관점에서 타입이 서로 호환되는지의 여부를 판단하는 것이다.

아래와 같이 capt에는 name속성이 존재하고 Avengers 인터페이스에서도 name 속성을 갖고 있기 때문에 타입 호환이 이루어진다.

2️⃣ 인터페이스 & 클래스 타입호환

아래 코드에서는 developer와 person의 타입호환이 이루어지지 않는 모습이다.

왜냐하면 Person클래스에는 skill이라는 속성이 존재하지 않아서 developer와 비슷한 구조를 갖지 않기 때문이다.

따라서, 타입의 호환을 위해서는 오른쪽의 타입이 더 많은 타입을 갖거나 구조적으로 더 큰 경우에 가능하다. (위의 경우 developer가 person보다 더 많은 타입과 큰 구조를 가졌었다.)

  • 방법1. Person 클래스에도 skill속성을 넣어 Developer와 같은 구조를 갖게하여 호환이 가능하게 한다.

  • 방법2. 반대로 구조가 큰 developer가 person에 할당되게 하여 호환이 가능하게 한다.

3️⃣ 함수 호환

파라미터(매개변수) 수가 더 많은 경우

  • 할당하는 함수가 sum일 경우 add보다 매개변수가 많으므로(구조적으로 크므로) 호환이 불가능하다.
  • 할당하는 함수가 add일 경우 sum보다 매개변수가 적으므로(구조적으로 작으므로) 호환이 가능하다.

파라미터(매개변수) 수가 같고 타입도 같은 경우

  • 모든 타입이 number로 같고, 매개변수가 같으므로(구조적으로 같으므로) 호환이 가능하다.

파라미터(매개변수) 수가 같지만 타입이 다른 경우

  • 타입이 number, string으로 다르므로 호환이 불가능하다.

4️⃣ 제네릭 호환

제네릭은 제네릭 타입 간의 호환 여부를 판단할 때 타입 인자 <T>가 속성에 할당 되었는지를 기준으로 한다.

인터페이스 내에 속성(member변수)가 없는 경우

  • x,y는 같은 타입으로 간주된다.

인터페이스 내에 속성(member변수)이 있는 경우

  • NotEmpty에 넘긴 제네릭 타입 <T>data 속성에 할당되었으므로 xy는 서로 다른 타입으로 간주된다.

💡 Tip) Soundness란?

: 타입스크립트는 컴파일 시점에 타입을 추론할 수 없는 특정 타입에 대해서 일단 안전하다고 보는 특성이 있다. 이걸 "들리지 않는다(it is said to not be sound)"라고 표현한다.

5️⃣ 이넘타입 호환

이넘 타입은 number 타입과 호환되지만 이넘 타입끼리는 호환되지 않는다.

6️⃣ 클래스타입 호환

클래스 타입은 클래스 타입끼리 비교할 때 스태틱 멤버(static member)와 생성자(constructor)를 제외하고 속성만 비교한다.

참조 및 참고하기 좋은 사이트

profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글