HTML&CSS&Javascript/TS

[Typescript] 타입스크립트 Type 정리

킹우현 2023. 8. 13. 02:23

In Typescript, a 'type' is a convenient way to refer to the different properties and functions that a value has.
타입이란 값이 가지고 있는 속성(프로퍼티)이나 함수(메서드)를 추론할 수 있는 편리한 방법이다.

 

서론

자바스크립트의 값들은 타입에 따라 각각의 프로퍼티와 메서드를 가지고 있다.

 

예를 들어 위와 같이 "apple" 이라는 값은 단순히 string 문자열이라고 설명할 수도 있지만, 조금 더 상세히 설명하자면 문자열이 가지는 프로퍼티와 메서드를 가지고 있는 값(Value)라고 할 수 있다.

 

자바스크립트의 문자열은 .length 와 같은 프로퍼티를 가지고, .toLowerCase()나 .split() 등과 같은 메서드를 가진다.

 

이렇듯 타입이란 어떠한 값이 가지고 있는 프로퍼티나 메서드를 추론할 수 있는 방법이다. 이제부터 타입스크립트에서 제공하는 Type에 대하여 알아보도록 하자.

 

Types in Typescript

타입스크립트는 자바스크립트에서 기본으로 제공하는 기본 자료형(Built-in types)을 상속한다. 타입스크립트의 타입 유형은 다음과 같이 분류된다.

  • 원시타입(Primitive Types) : string, number, boolean, null, undefined, symbol
  • 객체타입(Object Types) : object, array, classes, object

원시타입(Primitive Type)

1) String

let red: string = 'Red';

let green: string = "Green";
let yourColor: string = 'Your color is' + green;

let myColor: string = `My color is ${red}.`;

function strings(str1: string, str1: string): string {
	return str1 + str1;
}

작은따옴표('), 큰따옴표(") 뿐만 아니라 ES6의 템플릿 문자열도 string 타입에 포함 된다.

2) Number

let num: number;
let integer: number = 6;
let float: number = 3.14;

let hex: number = 0xf00d; // 61453
let binary: number = 0b1010; // 10
let octal: number = 0o744; // 484

let infinity: number = Infinity;
let nan: number = NaN;

function plus(num1: number, num2: number): number {
	return num1 + num2;
}

정적 타입이라 해서 C / JAVA 처럼 int, float, double 타입은 없고, Javascipt의 number 자료형을 그대로 사용한다.

16진수, 10진수, 2진수, 8진수 리터럴도 지원한다.

3) Boolean

let isBoolean: boolean;
isBoolean = true;

let isDone: boolean = false;

단순한 참(true) / 거짓(false) 값

4) Null / Undefined

let nullable: null = null;
let undefinedable: undefined = undefined;

자바스크립트에서도 undefined가 하나의 자료형이자 값이었던 것 처럼, 타입스크립트에서도 null과 undefined 타입이 있다.

 

기본적으로 null  undefined는 다른 모든 타입의 하위 타입으로 치부된다.즉, null과 undefined를 아무 여러 타입에 할당할 수 있다는 것을 의미한다.

 

/* strick 모드가 아닐경우에만 대입이 가능함 */
let num: number = undefined;
let str: string = null;
let obj: { a: 1, b: false } = undefined;
let arr: any[] = null;
let und: undefined = null;
let nul: null = undefined;
let voi: void = null;

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

 

객체 타입(Object Type)

1) Object

let obj: object = {};
let arr: object = [];
let func: object = function () {};
let nullValue: object = null;
let date: object = new Date();

자바스크립트에서의 object는 객체 뿐만 아니라 배열, 함수 까지 object로 포함한다. 타입스크립트에서도 마찬가지라서 기본적으로 typeof 연산자가 "object"로 반환하는 모든 타입을 뜻한다.

 

👉🏻 정확히 number, string, boolean, bigint, symbol, null, 또는 undefined가 아닌 나머지를 의미한다.

 

이처럼 여러 타입의 상위 타입으로 인식되기 때문에 타입스크립트에서 object를 그대로 타입으로 쓰기에는 애로사항이 많다.

 

let userA: { name: string, age: number };
userA = {
  name: 'HEROPY',
  age: 123
};

let userB: { name: string, age: number };
userB = {
  name: 'inpa',
  age: false, // Error
  email: 'inpa@naver.com' // Error
};

따라서 정말 객체에 타입을 지정해주고 싶다면 위와 같이 객체 속성(Properties)들에 대한 타입을 개별적으로 지정하는 식으로 사용하면 된다.

// type alias
type IUser = {
    name: string,
    age: number
}
 
let userA: IUser = {
    name: 'HEROPY',
    age: 123
};

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

let userA: IUser = {
  name: 'HEROPY',
  age: 123
};

하지만 이게 타입인지 객체인지 뭔지 가독성이 매우 좋지않은걸 느낄텐데, 이를 극복하기 위해 타입스크립트는 type 리터럴이라고 불리우는 alias 기능과 interface 라는 문법을 추가했다. (type 별칭과 인터페이스는 뒤에서 다룬다)

 

2) Array

let fruits: string[] = ['Apple', 'Banana', 'Mango'];
// or
let fruits: Array<string> = ['Apple', 'Banana', 'Mango'];

배열 타입은 두 가지 방법으로 쓸 수 있다.

 

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

 

추가 타입(Tuple / Enum / Any / Void / Never / Union / Unknown / Type Alias / Interface / Generic)

1) Tuple

let x: [string, number]; // 튜플 타입으로 선언

x = ["hello", 10]; // 성공
x = [10, "hello"]; // 오류 (원소 타입이 안맞음)
x = ["hello", 10, 99]; // 오류 (원소 갯수가 안맞음)

let user: [number, string, boolean] = [1234, 'HEROPY', true];
console.log(user[0]); // 1234
console.log(user[1]); // 'HEROPY'
console.log(user[2]); // true

/* 2차원 튜플 */
let users: [number, string, boolean][];
// or
let users: Array<[number, string, boolean]>;

users = [[1, 'Neo', true], [2, 'Evan', false], [3, 'Lewis', true]];

/* 타입 뿐만 아니라 값 자체를 고정 */
let tuple: [1, number];
tuple = [1, 2];
tuple = [1, 3];

tuple = [2, 3]; // Error - TS2322: Type '2' is not assignable to type '1'.

튜플은 배열의 서브타입으로, 크기와 타입이 고정된 배열 이라고 이해하면 된다.

 

let tuple: [string, number];
tuple = ['a', 1];
tuple = ['b', 2];

tuple.push(3);
console.log(tuple); // ['b', 2, 3];

tuple.push(true); // Error - 그렇다고 해서 튜플에 정의되지 않는 타입을 넣을수는 없다.
// push() 함수를 사용하여 요소를 추가하는 경우 처음 할당된 값의 타입과 정확히 일치해야 한다.

단, 유의할점은 튜플은 정해진 타입의 고정된 길이 배열을 표현하지만 이는 할당(Assign)에 국한 된다는 점이다. .push()  .splice() 등을 통해 값을 넣는 행위는 막을 수 없다. (이러한 행위가 되는 이유는 컴파일러의 멍청함이라고 치부하면 된다.)

 

2) Enum

// 상수 집합
enum Avengers { SpiderMan, IronMan, Thor, Hulk }

let hero: Avengers = Avengers.SpiderMan; // 0

// 시스템에서는 { SpiderMan = 0, IronMan = 1, Thor = 2, Hulk = 3 } 이런식으로 내부적으로 인덱싱이 자동으로 되어있다고 이해하면 된다.
enum Avengers { SpiderMan, IronMan, Thor, Hulk } 

let hero: Avengers = Avengers[0]; // 0

// 직접 인덱스 번호를 정한다면, 시스템에서는 { SpiderMan = 2, IronMan = 3, Thor = 4, Hulk = 5} 
// 이런식으로 내부적으로 인덱싱이 정해진 숫자 이후로 순차적으로 증가한다고 이해하면 된다.
enum Avengers { SpiderMan = 2, IronMan, Thor, Hulk }

let hero: Avengers = Avengers[2]; // SpiderMan
let hero: Avengers = Avengers[5]; // Hulk

enum은 C, Java와 같은 언어를 다뤄봤으면 한번쯤 들어보는 흔하게 쓰이는 타입으로 특정 값(상수)들의 집합을 의미한다.

 

위에서 배운 튜플 타입이 특정 타입이나 값을 고정하는 배열이라면, Enum은 특정 값을 고정하는 또다른 독립된 자료형이라고 보면 된다.

 

3) Union

let union: string | number;
union = 'Hello type!';
union = 123;
union = false; // Error - TS2322: Type 'false' is not assignable to type 'string | number'.
// 배열에 문자열과 넘버만을 허용하는데 튜플과 달리 자유로움
// 배열 타입을 UNION으로 표현할때 괄호로 묶어 표현 안그러면 string | number[] 이런식으로 됨
let array: (string | number)[] = ['Apple', 1, 2, 'Banana', 'Mango', 3];
// Or
let array: Array<string | number> = ['Apple', 1, 2, 'Banana', 'Mango', 3];
function padLeft(value: string, padding: boolean | number) {
  // ...
}

let indentedString = padLeft("Hello world", true);

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


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

 

4) Any

let any: any = 123;
any = 'Hello world';
any = {};
any = null;

let list: any[] = [1, true, "free"];
list[1] = 100;
// 명시적으로 any 타입 지정
let product_id: any = 124981;
product_id = 'p9023412'; // any 유형이 설정되었으므로 어떤 유형도 값으로 할당 가능

// 암시적으로 any 타입 지정
let product_id;
product_id = 124981;
product_id = 'p9023412';

any 타입은 단어 의미 그대로 모든 타입에 대해서 허용한다는 의미이다. 우리가 자바스크립트에서 사용하던 변수의 타입이 사실 any라고 봐도 무방하다.

 

any 타입이 존재하는 이유는 애플리케이션을 만들 때, 알지 못하는 타입을 표현해야 하는 경우가 존재할 수 있기 때문이다.

 

예를들어 사용자로부터 받은 데이터나 서드 파티 라이브러리 같은 동적인 컨텐츠에서 변수나 함수를 다룰때, 왠만하면 공식문서에 설명이 잘 되어있겠지만 그래도 알 수가 없을때 유용하게 사용된다.

 

하지만 그렇다고 any 타입을 남용하게 되면 타입스크립트를 사용하는 이유가 없어지기 때문에 자주 사용하지 말자.(그리고 무엇보다 타입스크립트 컴파일의 보호장치를 잃어버릴수도 있게 된다.)

 

5) Never

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

function fail() {
    return error("Something failed"); // 커스텀 에러함수 호출
}

const result = fail(); // 반환 타입이 never로 추론된다.

never 타입은 number나 string 처럼 어떠한 자료형 값을 담기 위한 타입이 아니다.

 

never 타입은 타입스크립트에서 잘못된 것을 알려주기 위한 키워드로써, 단어 그대로 절대 발생할 수 없는 타입을 나타낸다고 보면 된다. 또한 never는 에러를 반환하는 함수 표현식에서 항상 오류를 발생시키거나 절대 반환하지 않는 반환 타입으로도 쓰일수 있다.

 

6) Void

// return 값이 없는 함수
function hello(n: number): void {
  let sum: number = n + 1;
}

const hi: void = hello(1); // 값을 반환하지 않는 함수는 실제로는 undefined를 반환
console.log(hi); // undefined

void는 어떤 타입도 존재할 수 없음을 나타내기 때문에, any의 반대 타입 같다고 볼수 있다. 따라서 void는 보통 함수에서 반환 값이 없을 때 반환 타입을 표현하기 위해 쓰인다고 보면 된다.

 

만일 void를 함수가 아닌 변수 타입으로 정의한다면, void 타입 변수에는 undefined null만 할당이 가능해진다.

 

즉, void를 타입 변수를 선언하는 것은 유용하지 않다고 보면 된다.

 

7) Unknown

말그대로 Unknown은 알 수 없는 타입을 의미하며, any와 같이 모든 데이터 타입을 받을 수 있다.

 

그럼 모든 타입을 허용하면서 왜 any와 unknown을 구분했을까?

  • any는 어떤 것이든지 타입을 허용한다는 뜻이다.
  • unknown은 알 수 없다, 모른다의 어감이 강하다.

👉🏻 이둘의 차이점은 '엄격함' 에 있다.

 

let value : any = 10;
console.log(value.length); // undefined

예를 들어 any타입 같은 경우 위와 같이 number 타입의 데이터를 넣고 string 타입의 메소드 length를 사용했을때 어떠한 에러를 발생시키지 않고 그냥 undefined를 반환한다.

 

let valueNum: unknown = 10;
let valueStr: unknown = 'Test';

console.log(valueNum.length); // 문제 발생
console.log(valueStr.length); // 문제 발생

하지만 unknown 타입은 any 타입과 동일하게 모든 값을 허용하지만, 할당된 값이 어떤 타입인지 모르기 때문에 함부로 연산을 할 수 없다는 특징을 가지고 있다.

 

let valueNum: unknown = 10;

if (typeof valueNum === "number") {
  console.log(valueNum.length);
}

이런 경우 자바스크립트에서 자주 쓰이는 typeof 연산자를 통해 타입을 검사해서 해결할수 있다.

 

이처럼 any 대신 unknown 타입을 사용하면 체크를 해야 되는 코드는 많아지겠지만, 사전에 문제가 되는 부분을 방지할 수 있으므로 any 타입에 비해 안정적인 애플리케이션을 개발할 수 있게 된다.

 

8) Type Alias

let Dom: {version:string, el:(selector:string)=>void, css:(prop:string)=>void} = {
  version: '0.0.1',
  el(){},
  css(){}
};
type Operation = {
	version: string, 
    el: (selector:string) => void, 
    css: (prop:string) => void
}

let Dom: Operation = {
  version: '0.0.1',
  el(){},
  css(){}
};

타입 별칭(Type Alias)은 사용자가 정의하는 타입 변수 이다.

 

위와 같이 한줄로 복잡하고 기나긴 타입을 정의하면 가독성이 좋지 않기 때문에, type 별칭으로 타입 형태를 묶어둔 뒤 별칭을 타입명으로 선언해서 사용하는 방법이라고 보면 된다.

 

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

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

타입 별칭을 선언하는 방법은 마치 변수와 비슷하다.

 

흔히 C나 JAVA 처럼 앞에 자료형을 선언하고 뒤에 자료형에 부합하는 을 대입하는 것 처럼, 타입도 type 이라는 자료형을 선언하고 뒤에 타입값을 넣으면 된다.

 

9) Interface

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: 'Capt', 
  age: 28, 
  skill: 'typescript, javascript' 
};

logUser(person);

인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다. 좀 더 쉽게 말하자면 타입을 정의한 것들을 한데 모은 객체 타입이라고 말할 수 있다. 그래서 객체의 껍데기 혹은 설계도라고 불린다.

 

타입스크립트에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의할 수 있다.

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

이처럼 인터페이스는 단순히 타입 지정 뿐만 아니라 자체 확장하여 활용도를 높일수 있다는 장점이 있다.

✨ type alias vs interface 사용 선호도

타입 별칭과 인터페이스는 얼핏보면 비슷해 보이지만, 이들의 가장 큰 차이점은 '타입의 확장 가능 / 불가능 여부' 이다.

인터페이스는 다양한 방법으로 확장이 가능한데 반해 타입 별칭은 확장이 불가능하다. 따라서 간단한 타입 집합으로 이용할때는 type alias를 쓰고, 가능한 interface로 선언해서 사용하는 것을 추천되는 편이다.

 

10) Generic

type IsArray<T> = T[];

const numberArr: IsArray<number> = [1, 2, 3, 4, 5];
const stringArr: IsArray<string> = ['a', 'b', 'c', 'd', 'e'];
const mixArr: IsArray<string | number> = ['a', 2, 'c', 4, 'e'];

제네릭은 타입을 "변수화" 하였다고 생각하면 된다. 우리가 데이터를 직접 다루지않고, 변수라는 상자에 담아 사용하는 이유는 재사용성을 높이기 위해서다.

 

이처럼 타입을 마치 함수 파라미터 처럼 나중에 대입하도록 하여 재사용성을 높이기 위해 탄생한 타입이 제네릭 타입이라고 이해하면 된다.

 

제네릭의 선언 문법을 <T> 꺾쇠 괄호 기호를 이용한다. 안의 T 는 변수명으로서 아무 문자로 명명해줘도 상관없다.


참고 : https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8-%EC%A2%85%EB%A5%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC#%ED%83%80%EC%9E%85_-_unknown

 

📘 타입스크립트 타입 선언 & 종류 💯 총정리

타입 - Boolean 단순한 참(true) / 거짓(false) 값 let isBoolean: boolean; isBoolean = true; let isDone: boolean = false; 타입 - Number 정적 타입이라 해서 C / JAVA 처럼 int, float, double 타입은 없고, Javascipt의 number 자료형을

inpa.tistory.com