🎈 타입 단언 알아보기

var a;

이렇게만 선언한 a에 대한 타입은 any가 되고

a = 20;

이렇게 숫자로 대입한 a는 숫자로 추론이 된다.

a = 'a';

얘는 문자열로 추론이 된다.

var b = a;

얘는 원래같으면 맨 처음에 선언된 any가 b에 할당된다.

var b = a as string;

이렇게 as 키워드를 사용하여 후에 타입을 선언하면 선언 한 타입(string)으로 지정된다. 이처럼 타입 단언은 타입스크립트보다 개발자가 타입을 더 잘 알고있다라는 의미로 타입스크립트 너는 신경쓰지 말고 내가 정한 타입에 간주하라... 요런 너낌으로 받아들이면 된다.

 

🎈 DOM API 조작

<div id="app">hi</div>

var div = document.querySelector('div');

if (div) {
  div.innerText
}

웹페이지에 태그를 조작해줄 때 가장 많이 사용하는 API는 querySelector이다. 그리고 위 코드처럼 DIV라는 변수에 담아 태그에 접근하면 알아서 HTMLElement라고 추론해준다. 그리고 if문으로 div가 있는지 확인을 하고 그 다음에 조작을 해주는게 일반적인 타입단언의 패턴이다. div라는 값이 null일 수도 있기 때문에 밑에 호출된 div가 어떤 걸 데려오는지 div가 있는지를 if문을 통해 확인해보는 것이다.

 

그래서 이때,

var div = document.querySelector('div') as HTMLDivElement;
div.innerText;

이렇게 as라는 키워드를 사용하여 미리 HTMLElement로 타입을 단언해주는 것이다. as를 쓰는 시점에는 코드가 돌아갈 때 querySelector가 HTMLElement라고 단언해준다. 

 

🎈 타입 가드 알아보기

interface Developer {
  name: string;
  skill: string;
}

interface Person {
  name: string;
  age: number;
}

function introduce(): Developer | Person {
  return { name: 'Tony', age: 33, skill: 'Iron Making'}
}

var Tony = introduce();
console.log(Tony.name);

if ((Tony as Developer).skill) {
  var skill = ((Tony as Developer).skill);
  console.log(skill);
} else if ((Tony as Person).age) {
  var age = (Tony as Person).age;
  console.log(age);
}

인터페이스 선언해주고, introduce라는 함수에 유니온 타입으로 작성하여 Tony를 호출해줬다. 그리고 if문을 사용하여 타입 단언 방식으로 as를 써가며 타입이 있나 없나 확인하는 과정이다. 첫 줄에는 Tony as Developer를 사용하여 디벨로퍼에  skill이 있나 없나 확인을 한 후에 있으면 또 skill이라는 것을 호출해주도록 변수에 담는 이 과정을 반복해서 만들었다. 이렇게 하면 가독성도 떨어지고 여러번 선언해야 된다는 불편한 점이 있어 이것을 보완한 것이 타입 가드이다.

 

🎈 타입 가드 정의

function isDeveloper(target: Developer | Person): target is Developer {
  return (target as Developer).skill !== undefined;
}
if (isDeveloper(Tony)) {
  console.log(Tony.skill) 
} else {
  console.log(Tony.age) 

타입가드 함수는 is라는 패턴과 target이라는 키워드를 사용한다. 그리고 skill이라는 값이 있을 때 !== undefined 함수를 사용했다. if문은 토니가 Developer이면 스킬을 제공해준다. 그렇지 않으면 토니는 Person으로 인식하여 age를 제공해준다. if문 첫 줄에는 developer가 토니이고, 밑에줄은 Person으로서의 토니이기 때문에 각각의 필요한 속성을 접근할 수 있다.

 

🎈 타입추론 특징

let x = 3;

위와 같이 x 변수가 따로 타입을 지정하지 않더라도 일단 x는 3이라는 수식어가 들어있기에 number 타입으로 간주된다. 이렇게 변수를 선언하거나 초기화 할 때 타입이 추론된다. 이외에도 변수, 속성, 인자의 기본값, 함수의 반환값 등을 설정할 때 타입 추론이 일어난다.

var a = 'abc';

function getB(b = 10) {
  var c = 'hi';
  return b + c;
}

변수 C의 속성은 'hi' 라는 문자열이 들어있어 타입이 알아서 string 문자열로 자동 추론된다.

그리고 반환값은 b + c 인 경우

b: number, c:string

으로 간주되어 출력되는 값은 //10hi 로 출력된다. 그리고 getB 파라미터에 타입을 정의되지 않은 채 b만 정의가 될 경우 알아서 any라는 타입으로 정의해준다.

 

🎈 타입추론 기본 예제 02

interface Dropdown<T> {
  value: T;
  title: string;
}

var shoppingItem: Dropdown<string> = {
  value: 'abc',
  title: 'hello',
}

인터페이스와 제너릭을 이용한 타입 추론 방식이다. T에 따라서 value값이 변경 될 수도 있다. title이란 속성값은 string으로 고정시켜 놓았으니 그 타입 고대로 Fix 된 것. 그리고 다른 함수를 선언하면서 변수를 초기화 하면 타입도 초기화 된다. 위 코드처럼 value값에 'abc'를 넣으면 자동으로 문자열로 변동되고 숫자를 넣으면 자동으로 number 타입으로 변동된다.

 

🎈 타입추론 기본 예제 03 : 복잡한 구조에서의 타입 추론 방식

interface Dropdown<T> {
  value: T;
  title: string;
}

interface DetailedDropdown<K> extends Dropdown<K> {
  description: string;
  tag: K;
}

var DetailedItem: DetailedDropdown<string> = {
  title: 'hello',
  description: 'ab',
  value: 'a',
  tag: 'a'
}

처음에는 인터페이스로 제너릭을 선언해주고 value값도 T 제너릭 형태로 타입정의. 그리고 또 다른 인터페이스를 선언할 때는 Dopdown 인터페이스를 extends(확장) 키워드를 써서 데려오면 굳이 선언하지 않아도 암묵적으로 Dropdown에 있는 속성들이 들어와있다. (value, title) 그리고 새로운 DetailedItem 함수를 생성할 때 DetailedDropdown을 가져옴과 동시에 즉시 타입을 지정해주면 위에 있는 인터페이스들 value값들이 자동으로 string으로 변동된다. 그리고 DetailedItem 변수에 담긴 속성들은 문자열로 나열해주어야 에러를 방지할 수 있다.

 

🎈 가장 적절한 타입

타입은 보통 몇 개의 표현식을 바탕으로 타입을 추론한다. 그리고 그 표현식을 이용하여 가장 근접한 타입을 추론하게 되는데 이 가장 근접한 타입을 Best Common Type이라고 한다.

  • 예제
let arr = [0, 1, null];

배열에 들어가는 값들이 한 타입으로 통일되지 않았다. 2개는 숫자형, 1개는 논리형으로 들어가있는데 위 코드처럼 선언 후 arr 변수 위에 마우스 커서를 올려 놓으면 number | boolean 값으로 알아서 유니온 방식으로 추론해준다.

🎈 제너릭이란 ?

제너릭은 C#, JAVA등의 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징입니다. 특히, 한가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트를 생성하는데 사용됩니다.

 

🎈 제너릭 기본 문법

function logText<T>(text: T): T{
  console.log(text);
  return text;
}
logText<string>('하이');

이처럼 반환해주는 값을 '하이' 라고 문자열로 지정했으면,  logText에서 받은 값과 반환되는 값 모두 문자열로 지정해준다.

function logText 👉🏻 <T>(text: T): T{

이게 바로 제너릭의 기본 문법이다. <T>(text: T) : T

  • 어떤 타입을 받을 건지 먼저 정의하기 <T>
  • 그랬을 때 받은 타입을 인자로 넘기겠다고 정의하기 (text: T)
  • 그걸 리턴할 때도 그대로 쓰겠다는 의미 T

 

function logText1<T>(text:T) : T {
  console.log(text);
  return text;
}

const str = logText1<string>('a');
str.split('');
const login = logText1<boolean>(true);

이렇게 하면 로그인 값은 논리형으로 인식해준다. str 변수는 문자열, login 변수는 논리형 값이라는 것을 타입스크립트도 알고 우리도 한 눈에 확인해서 알 수 있으므로 타입이 틀어지지 않게 잘 구성해 나갈 수 있는 것이 바로 제너릭의 장점이다.

=> 쉽게 말해서 그 타입에 뭐가 들어갈거다 라는 것을 호출한 시점에 바로 정의하는 것이 제너릭의 장점

 

🎈 인터페이스 제너릭 사용하기

인터페이스에서 제너릭을 이용하여 코드를 최적화 시켜보았다. 원래는 인터페이스 선언할 때 기본 value값을 타입으로 정의해 주어야 했다면 제너릭을 이용하면 const 함수 선언할 때와 동시에 타입을 정의해주면 되고 이처럼 타입스크립트의 너릭은 편리하고 코드도 최적화 시킬 수 있다. 제너릭은 장점이 증말 많은 듯(?) 하다🤗

interface Dropdown<T> {
  value: T;
  selected: boolean;
}

const obj: Dropdown<string> = {value: '123', selected: false };

Dropdown 뒤에 제너릭<T>을 선언해준다. value값도 T로 지정. 그럼 추후에 변수를 호출해줌과 동시에 문자열로 받고싶으면 Dropdwon에 <string>을 선언해주고 값도 똑같은 형식으로 value: '123'으로 작성한다. 그럼 알아서 인터페이스 value값이 string으로 인식해준다. 이렇게 제너릭은 꼭 인터페이스에 타입을 선언하지 않고도 그때그때마다 필요한 타입들을 함수 뒤에 <string>이런식으로 선언해서 사용해주면 된다.

 

🎈 제너릭의 타입 제한 - 정의된 타입 이용하기

 

  •  방법1
function logTextLength<T>(text:T[]):T[] {
  console.log(text.length);
  text.forEach(function (text) {
    console.log(text);
  })
  return text;
}
logTextLength<string>(['hi', 'abc']);

콘솔 로그 파라미터에 text.length를 선언해주었는데, length인지를 제대로 알게 해주려면 T 제너릭 타입에 힌트를 줘야한다. 그 힌트는 바로 [ ] 배열 표시를 넣어주면 된다. 그리고 함수를 넘길 때도 반드시 [ ] 배열 형식으로 넘겨 주기!

 

  • 방법2
interface LengthType {
  length: number;
}

function logTextLength01<T extends LengthType>(text:T):T {
  text.length;
  return text;
}
logTextLength([1,2,3,4,5]);

[ ] 배열 기호를 안써주고 length 타입이 선언됐다는 것을 표시하려면 제너릭을 선언할 때 extends 키워드를 사용하고 LengthType을 선언해줘도 된다. 그럼 더 구체적으로 length를 사용하는구나~~ 라고 알 수 있다.

 

🎈 제너릭의 타입 제한 - KeyOf

interface ShoppingItem {
name: string;
price: number;
stock: number;
}

function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption:T):T {
return itemOption;
}
getShoppingItemOption('name');

선언 된 인터페이스 속성중 하나만 받겠다로 밑에 함수를 제약할 수 있다.

 

🎈 사용방법정리

  1. ShoppingItem 라는 인터페이스를 선언하고 그 안에 속성들을 정의해준다.
  2. getShoppingItemOption 함수를 만들면서 제너릭을 선언해준다.
  3. 제너릭 선언 시 extends 확장 키워드를 사용하고 keyof라는 예약어로 ShoppingItem을 불러온다.
  4. 파라미터 값에는 반환받을 값(itemOption)을 넣어준다.
  5. 마지막, getShoppingItemOption 함수를 불러내고 [컨트롤 + 스페이스바] 누르면 인터페이스 안에 담긴 속성들을 불러올 수 있다.  그 속성들 중 나는 name만 불러오고 싶어 name만 불러왔다.

 

 

+ Recent posts