타입스크립트 타입정리

이셀·2024년 3월 11일
0

타입스크립트를 공부하는 이유

사내에선 js와 react를 주로 사용하나 ts는 사용하지 않다보니 아쉬움이 있었다.
다른 프로젝트에선 ts가 기본적이기도 하고 이후에 next.js를 배울 경우 큰 도움이 될까 싶어 업무를 보면서 남는 시간동안 타입스크립트에 대해 공부하는게 좋을 것 같아 짬짬이 공부한 기록들을 남겨두고자 한다.

아마 정리하면서 많은 수정을 거치며 버전이 달라지겠지만... 일단 기록해본다:)

타입스크립트란 ?

타입스크립트는 자바스크립트에 타입을 부여한 것으로 자바스크립트의 단점을 보완해줄 수 있다.

자바스크립트는 런타임에 에러를 띄우지만 타입스크립트는 컴파일(브라우저에서 파일을 변환하는 과정)단계에서 에러를 띄우기 때문에 생산성적으로 효율이 좋다.

타입스크립트의 타입

위에서 말한대로 ‘타입을 부여’ 해주기 때문에 변수, 함수에 타입을 정의한다.

크게 원시타입, 특별한 타입, 기타로 나눈다.

  • 원시타입(Primitive Type)
    원시타입의 종류로는 string, number, boolean, null, undefined, literal 타입이 있다.

STRING(문자열)


변수 타입이 문자열인 경우 선언. ‘,”,` 모두 string 타입에 포함.

	let str: string = 'Hello World';

NUMBER(숫자)


변수 타입이 숫자인 경우 선언.

	let str: number= '20240311';

BOOLEAN(참/거짓)


변수 타입이 참 혹은 거짓인 경우 선언.

	let str: boolean= true;

ARRAY(배열)


변수 타입이 문자열, 숫자만 가지는 배열일 경우 선언. 선언할 경우 두 가지 방법으로 쓸 수 있다.

첫 번째 방법은 타입 뒤에 [] 를 쓰는 것이고 두 번째 방법은 Array<> 배열 타입을 쓰는 것이다.

	// 문자열
	let arr: string[]= ['Hello World'];
		
	// 숫자
	let arr: number[]= [1,2,3];
	
	// Array<> 배열타입 사용 시
	let arr: Array<number> = [1,2,3]

TUPLE(튜플)


원소의 수(크기)와 타입이 정확히 지정, 길이가 고정되어 있는 배열 형식.

배열과 유사(배열의 서브타입)하지만 큰 차이점은 명시된 개수만큼의 원소를 가질 수 있다는 점이다. 즉, 정해진 타입의 고정된 길이(length) 배열을 표현한다.

이 이야기는 할당(Assign)에 국한되는 것으로 .push(), .splice() 등을 통해 값을 넣는 행위는 막을 수 없다(이게 가능한 이유는 컴파일러의 필터링이 정확하지 않기 때문이다.)

그리고 정의하지 않은 타입, 인덱스로 접근하면 오류가 난다.

let arr: [string, number]= ['Hello World', 20240311];

// 튜플의 활용 예시 : rgb
let rgbColor: [number, number, number] = [255, 255, 0];
// 타입이 아닌 값을 고정하는 예시
let tuple:[1, number];
tuple=[1,2];
tuple=[1,3];

// 1이 있어야할 자리에 2가 있으므로 에러
tuple=[2,3]; 

ENUM(이넘)


이넘은 C, JAVA와 같은 언어에서 흔하게 쓰이는 타입으로 특정 값(상수)들의 집합을 의미한다.

즉, 특정 값을 고정하는 또다른 독립된 자료형이라고 볼 수 있다.

숫자 또는 문자열 집합에 이름을 부여할 수 있고 인덱스 번호로도 접근이 가능하며 이 것을 사용자 편의로 변경해서 사용도 가능하다.

기본적으로 인덱스 번호는 0부터 시작하여 1씩 증가하며 반드시 0으로 시작할 필요가 없다.

또한 역방향 매핑도 지원하며 서로의 데이터, 이름에 접근이 가능하다.

문자열 값으로도 초기화가 되지만 이 경우엔 역방향 매핑을 지원하지 않는다.

// 상수의 집합
enum fruits { Apple, Banana, Melon, Strawberry }

let Fruit: fruits = fruits.Apple; // 이 경우 0이 리턴된다.
// 인덱스로 접근시
let Fruit: fruits = fruits[0];

OBJECT(객체)


TS의 객체는 객체, 배열, 함수 모두 object로 부른다.(JS도 마찬가지)

여러 타입의 상위타입으로 인식되기 때문에 되도록이면 자세하게 타입을 지정해주는 것이 좋다.

다만 가독성으로 인하여 type literal(literal = 변하지 않는 데이터)로 불리는 alias와 interface 문법이 추가되었다.

ALIAS(별칭)


별명, 별칭이라고 불리우며 사용자가 정의하는 타입 변수이다.

한줄로 복잡하고 긴 타입을 정의하면 가독성이 좋지 않아 별칭을 타입명으로 선언해서 사용하는 방법이다.

보통 별칭을 사용할 경우 변수명은 대문자로 사용하며 선언 방식은 변수와 비슷하다.

// type 변수명 = 타입;
type Name = string;
type Age = number;

let name: Name = 'selu';
let age: Age = 20;

INTERFACE(인터페이스)


상호 간에 정의한 약속 혹은 규칙을 의미한다.

즉, 타입을 정의한 것들을 모은 객체 타입으로 객체의 껍데기 혹은 설계도하고 불린다.

대게 정의하는 범주들은 다음과 같다.

  1. 객체의 스펙(속성과 속성타입)
  2. 함수의 파라미터
  3. 함수의 스펙(파라미터, 반환 타입 등)
  4. 배열과 객체를 접근하는 방식
  5. 클래스

이처럼 인터페이스는 단순하게 타입만을 지정하는 것이 아니라 자체 확정해서 활용도를 높일 수 있다.

보통 인터페이스를 사용할 경우 변수명은 대문자로 사용한다.

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

interface Developer extends Person { // 인터페이스 상속
  skill: string;
}

function logUser(obj: Developer) {
  console.log(obj.name);
  console.log(obj.age);
  console.log(obj.skill);
}

let person = { 
  name: 'selu', 
  age: 20, 
  skill: 'js, ts, react, oracle' 
};

logUser(person);

alias와 interface의 사용

별칭과 인터페이스는 비슷해보이겠지만 핵심적인 차이점은 타입의 확장 가능 여부이다.

인터페이스는 새 프로퍼티로 확장할 수 있지만 별칭은 불가능하다.

그래서 간단하게 이용할 때엔 별칭을 쓰지만 되도록이면 interface를 사용하는 것을 추천한다.

ANY(애니)


모든 타입에 대해 허용하는 타입. 존재하는 이유는 알지 못하는 타입을 표현해야 하는 경우가 있기 때문이다.

예를 들어 사용자로부터 받은 데이터나 서드 파티 라이브러리와 같은 동적 컨텐츠에서 변수, 함수를 다룰 때 유용하게 사용된다.

단 너무 남용해서 사용하면 안된다. 많이 사용하는 경우 컴파일을 할 때 경고를 주지 않는 경우가 생긴다.

UNKNOWN(언노운)


알 수 없는 타입을 의미한다. Unknown은 Any와 다르게 다른 타입에는 할당할 수 없으며 구분한 이유는 크게 두 가지가 있다.

  1. Any는 어떤 것이든 허용한다.
  2. Unknown은 알 수 없다, 모른다 에 가깝다.

즉, 이 두 가지 타입의 차이점은 ‘엄격함’이다.

NULL&UNDEFINED


두 타입 모두 모든 타입의 하위 타입으로 볼 수 있다. 즉, 아무 타입에 할당할 수 있다는 것을 의미한다.

단 tsconfig.json에서 strick모드가 아닐 경우에만 가능하다.

NEVER


Never은 자료형 값을 담기 위한 타입은 아니고, 단어 말대로 절대로 발생할 수 없는 타입을 의미한다.

또한 Never은 에러를 반환하는 함수 표현식에서 항상 오류를 발생시키거나 절대 반환하지 않는 반환 타입으로도 사용할 수 있다.

// 에러를 발생시키는 커스텀 never 타입 함수를 생성
function error(message: string): never {
    throw new Error(message); // 함수는 리턴되지 않고 에러를 throw함
}

function fail() {
    return error("Failed"); // 커스텀 에러함수 호출
}
// 결과
const result = fail();

Never역시 모든 타입에 할당 가능한 하위타입이지만 반대로 어떤 타입도 Never에 할당될 수 없다.(Never 사진은 예외)

VOID


Void는 어떤 타입도 존재할 수 없음을 나타내기 때문에 Any와 정반대 개념이라고 볼 수 있다.

따라서 보통 함수에서 반환 값이 없을 때 타입을 표현하기 위해 쓴다.

만약 Void를 함수가 아닌 변수로 정의한다면 Unfinded와 Null만 할당이 가능하다.

let unusable: void = undefined;
unusable = null; // 단 tsconfig.json에서 strick모드가 아닐 경우에만 가능

LITERAL


문자열과 숫자에 한해 값 자체를 타입으로도 선언이 가능하며 각각 숫자, 문자 리터럴타입으로 나눈다.

  • 숫자 리터럴 타입(Numeric Literal Types)
    const num = 3; // 타입은 :number이 아닌 타입 3 으로 찍힌다.
  • 문자 리터럴 타입(String Literal Types)
    type Easing = 'ease-in' | 'ease-out' | 'ease-in-out';
    
    function animate(dx: number, dy: number, easing: Easing) {
       if (easing === 'ease-in') {
          // ...
       } else if (easing === 'ease-out') {
       } else if (easing === 'ease-in-out') {
       } else {
          // null이나 undefined를 전달하면 필터링 실행
       }
    }
    
    animate(0, 0, 'ease-in');
    animate(0, 0, 'uneasy'); // 오류: "uneasy"는 여기서 허용하지 않습니다

UNION(유니언)


2개 이상의 타입을 허용하는 경우를 유니언이라고 한다.(OR 의 의미로도 쓰인다.)

| (파이프)를 통해 타입을 구분하며, 괄호는 단일 타입 때 씌우지 않아도 되지만 배열일 경우 사용한다.

let array: (string | number)[] = ['Apple', 1, 2, 'Banana', 'Mango', 3];
// Or
let array: Array<string | number> = ['Apple', 1, 2, 'Banana', 'Mango', 3];

유니언은 타입이 A도 될 수 있고, B도 될 수 있기 때문에 사용하지만 타입에러를 낼 수 있다.

이 부분은 함수 오버로딩을 통해 해결이 가능하다.

  • 오버로딩

INTERSECTION(인터섹션)


&를 사용하여 2개 이상의 타입을 조합할 때 인터섹션이라고 한다.

유니언을 OR 과 같이 이해한다면 인터섹션은 AND로 이해할 수 있다.

인터섹션은 새로운 타입을 생성하지 않고 확장할 때 사용하기 때문에 자주 사용되진 않는다.

차라리 새로운 타입을 하나 만들어 할당하는 것을 추천한다.

type Person = {
   name: string;
   age: number;
};
type Developer = {
   name: string;
   skill: number;
};

const heropy: Person = {
  name: 'Heropy',
  age: 36,
  skill: 11 // Error - { name: string; age: number; skill: number; }' 형식은 'person' 형식에 할당할 수 없습니다. 개체 리터럴은 알려진 속성만 지정할 수 있으며 'person' 형식에 'skill'이(가) 없습니다
};

// 두 타입 별칭을 합쳐 하나의 { name: string, age: number, skill: number } 이라는 타입을 구성한다.
const neo: Person & Developer = {
   name: 'Neo',
   age: 85,
   skill: 11,
};

Intersection 과 Mixin 의 차이점?

두 타입은 재사용성이 높은 컴포넌트로부터 함수와 클래스를 빌드한다는 공통점이 있다.

하지만 intersection은 원래의 타입을 대체/변경하지 않고 특정 속성을 확장하고 싶을 때 사용한다. 그래서 실제로 return된 것은 새로운 인스턴스라고 볼 수 있다.

mixin의 경우 다른 클래스를 상속받을 필요 없이 클래스에서 제공하는 기능을 추가하여 정의를 변경한다. 따라서 이미 만들어진 것들과 앞으로 만들어질 것들에게 영향을 미친다.

GENERAL(제네릭)


제네릭은 타입을 변수화한것으로, 제네릭을 사용하는 것은 재사용성을 높이기 위해서다.

타입을 함수의 파라미터처럼 대입하게 하여 사용하며 선언문법을 로 사용한다.

여기서 T는 변수명으로 아무 문자로 명명해주어도 상관없다.

profile
프론트엔드 개발 과정을 기록 중입니다:)

0개의 댓글