영벨롭 개발 일지

[TypeScript] 타입 선언 및 종류 본문

Programming Language/TypeScript

[TypeScript] 타입 선언 및 종류

영벨롭 2023. 12. 24. 23:23

📌 타입스크립트의 Type

타입스크립트는 자바스크립트의 타입(primitive, object types)을 그대로 제공하면서, 추가적으로 제공하는 타입들이 있습니다. 

Primitive types Object types
boolean, number, string, null, undefined, symbol function, array, object, classes
추가 제공 타입
tuple, enum, any, void, never, union, unknown

📌 Type Annotation & Inference

✔️ Type Annotation 

 Type annotation 은 개발자가 직접 타입을 지정하는 것을 의미합니다. 

 일반 변수, 함수의 매개 변수(parameter), 객체 속성 등에 :TYPE 을 붙여 타입을 지정합니다. 

let num: number = 1;
let obj: { name: string, age: number };
function func(a: number, b: number): number {
	return a + b;
}

 

✔️ Type Inference 

 Type inference 는 타입스크립트가 알아서 타입을 추론하는 것입니다.

 예를 들어 let num = 1; 과 같이 변수를 선언과 동시에 초기화하게 된다면, 타입스크립트는 변수 num 을 자동으로 number 타입으로 추론하게 됩니다. 

let num = 1; // number 타입으로 추론함
let str = "hello world"; // string 타입으로 추론함

 

 

✅ Type annotation 이 꼭 필요한 경우!

 타입스크립트가 타입을 추론하지 못하여 Type annotation 이 꼭 필요한 경우는 다음과 같습니다. 

 

- any 타입을 리턴받는 경우 

다음 예제를 보시면 JSON.parse 메소드는 any 타입을 리턴합니다. 이때 type annotation 을 하지 않은 coord1 역시 any 타입으로 지정됩니다. 

const json = '{"x": 1, "y": 2 }';
const coord1 = JSON.parse(json); // any
const coord2: { x: number; y: number } = JSON.parse(json); // { x: number, y: number }

 

- 변수 선언을 먼저하고, 초기화를 나중에 할 경우

  앞서 언급했듯 변수의 선언과 동시에 초기화를 할 경우 타입스크립트가 해당 변수의 타입을 추론합니다.  

 하지만 초기화를 나중에 할 경우, 타입을 추론하지 못하고 any 타입으로 지정됩니다. 

let tmp1 = 1; // number
let tmp2;
tmp2 = 1; // any

 

- 변수에 대입될 값이 일정하지 않은 경우

  다음과 같이 변수에 대입될 값이 일정하지 않은 경우에도 type annotation 이 꼭 필요합니다. 

let tmp1 = 1;
tmp1 = false; // error

let tmp2: number | boolean = 1;
tmp2 = false;

📌 Boolean Type

boolean 타입은 참(true) 또는 거짓(false) 을 값으로 갖습니다. 

let bool1: boolean
bool1 = true;
let bool2: boolean = false;

📌 Number Type

 number 타입은 정수, 실수, 2진수, 16진수, 8진수, NaN, Infinity 등의 숫자를 값으로 갖습니다. 

let num: number;
let integer: number = 1;
let float: number = 1.23;
let hex: number = 0xfff;
let binary: number = 0b010;
let octal: number = 0o123;
let inf: number = Infinity;
let nan: number = NaN;

📌 String Type

string 타입은 큰 따옴표(""), 작은 따옴표(''), ES6의 템플릿 문자열로 표현할 수 있습니다. 

let str: string;
let str1: string = "a";
let str2: string = "b";
let str3: string = `${str1}${str2}c` + "d";

📌 Array Type

 배열 타입은 두 가지 방법으로 선언할 수 있습니다. 

  1. Type[]
  2. Array<Type>

 또한 anyunion 타입을 통해 여러 타입의 값을 요소로 가질 수 있습니다. 

 또한 readonly 또는 ReadonlyArray 를 사용하여 읽기 전용 배열을 생성할 수 있습니다. 읽기 전용으로 생성한 배열에 요소를 추가하거나 수정하는 연산을 진행할 경우 에러가 발생합니다. 

// 한 가지 타입을 가지는 배열
let names1: string[] = ["John", "Kim"];
let names2: Array<string> = ["John", "Kim"];

// 여러 타입을 가지는 배열(유니언 타입 사용)
let arr1: (string | number)[] = [1, "John"];
let arr2: Array<string | number> = ["John", 1];

// 여러 타입을 단언 X
let someArr: any[] = ["John", 1, [], {}, false];

// 읽기 전용 배열 생성
let strArr: readonly string[] = ["A", "B"];
let numArr: ReadonlyArray<number> = [1, 2];
// strArr.push('C') // 컴파일 에러
// numArray[0] = 3; // 컴파일 에러

📌 Tuple Type

 튜플은 배열의 서브타입입니다. 배열과 유사하지만, 크기와 요소의 타입 및 순서가 고정되어 있습니다. 

 튜플의 선언은 [ Type1, Type2, ... ] 와 같이 할 수 있습니다.

let tup: [string, number];
tup = ["a", 1];
tup = [1, 'a'] // error: 타입 불일치
tup = ['a', 1, 2]; // error: 크기 불일치

 

 2차원 튜플은 다음과 같이 선언할 수 있습니다. 

let tup1: [number, string][];
let tup2: Array<[number, string]>;

tup1 = [[1, "Hello"], [2, "World"]];
tup2 = [[1, "Hello"], [2, "World"]];

 

 튜플의 타입 뿐만 아니라 값 자체를 고정시킬 수도 있습니다. (강한 타입) 

 이때 타입이 일치하더라도, 고정된 값이 아닌 값을 넣으면 에러가 발생합니다. 

let tup: [1, string];
tup = [1, "Hello"]; // OK
tup = [2, "World"]; // Error

 

 이때 유의할 점은 튜플이라 하더라도 push 메소드를 이용하여 값을 넣는 것은 허용됩니다. 

let tup: [string, number];
tup = ["a", 1];
tup.push(2); // tuple 이라고 하더라도 push 메소드를 이용해서 값을 넣는 것은 허용!!!

📌 Enum Type

 enum 타입은 값들의 집합을 명명하고 이를 사용하도록 만드는 자료형이라고 보면 됩니다. 이때 주의해야할 점은 enum 의 속성 값으로는 숫자문자열만 허용됩니다. 

 

 enum 타입은 배열의 인덱스와 같이 멤버들의 값을 0부터 차례대로 지정하는 특징을 갖고 있습니다. 

enum Direction {
  Up, // 0
  Down, // 1
  Left, // 2
  Right, // 3
}

let dir: Direction;
dir = Direction.Up; // 0
dir = Direction["Down"]; // 1

let dirKey: string = Direction[0]; // 'Up'

console.log(Direction)

 

 하지만 이는 기본 값일 뿐, 개발자가 직접 값을 명시할 수 있습니다. 값을 변경한 부분부터 다시 1씩 증가하며 지정됩니다.

enum Direction {
  Up, // 0
  Down, // 1
  Left = 100, // 100
  Right, // 101
}

 

 속성 값을 문자열로 지정할 경우, 자동 증가가 되는 숫자와 달리 모든 속성에 대해 값을 지정해주어야 합니다. 

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

let dir1: Direction = Direction.Up; // "UP"
let dir2: Direction = Direction["Down"]; // "DOWN"

 

✅ enum 과 객체의 차이점

- 객체는 코드내에서 새로운 속성을 자유롭게 추가할 수 있지만, enum 은 선언할 때 이후에 변경 불가
- enum 의 속성값으로는 문자열과 숫자만 허용됨

📌 Object Type

 자바스크립트와 마찬가지로 타입스크립트에서도 객체, 배열, 함수까지 object 타입에 포함됩니다. 

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

 

 이처럼 여러 타입이 object 타입으로 인식되기 때문에, 다음과 같이 object 타입에 객체값을 주고 속성을 조회할 경우 에러가 발생합니다. 이는 object 타입에 id 속성이 없음으로 인식되기 때문입니다. 

const obj: object = { id: 1, title: "hello" };
console.log(obj.id); // Error

 

 따라서 object 타입이 아닌, 지정하고자 하는 객체의 속성들에 대한 타입을 개별적으로 지정하여 사용해야 합니다. 

let obj: { id: number; title: string };
obj = {
  id: 1,
  title: "hello",
};

📌 Union Type

 2개 이상의 타입을 지정하고자 할 때 사용하는 타입으로, | (파이프) 기호를 통해 타입을 구분합니다. 

 ( Type1 | Type2 | ... ) 과 같이 유니온 타입을 지정합니다. 

let union: string | number;
union = 1;
union = "a";

let arr: (string | number)[] = ["Hello", 1, "World"];

📌 Any Type

 any 타입은 모든 타입에 대해 허용한다는 의미를 가집니다. 알지 못하는 타입을 표현할 때 유용하게 사용됩니다. 

let any: any = "abc";
any = 1;
any = [];
let any2: any = any;

📌 Unknown Type

 unknown 타입은 any 타입과 같이 모든 데이터 타입을 허용합니다. 하지만, 다른 변수의 값으로는 할당해줄 수가 없습니다. 

let un: unknown = false;
let bool: boolean = un; // error

let value: unknown = "Hello";
let str = value;
console.log(str.length); // error

 

 이때 타입 단언을 사용하여 unknown 타입의 변수를 다른 변수의 값으로 할당할 수 있습니다. 

let un: unknown = false;
let bool: boolean = un as boolean; // OK

let value: unknown = "Hello";
let str = value as string;
console.log(str.length); // OK

 

✅ any와 unknown 의 차이점

any: 모든 타입 허용, 타입 검사를 느슨하게 한다.
unknown: 모든 타입 허용, 타입 검사를 엄격하게 한다. 

 

예를 들어, number 타입으로 정의된 변수 num 으로부터 num.length 출력하는 코드를 실행하게 되면 에러가 발생합니다. 이는 number 타입에 length 프로퍼티가 존재하지 않기 때문에 발생하는 오류입니다. 

let num: number = 10;
console.log(num.length); // Error: 'number'형식에 'length' 속성이 없습니다.

 

 이를 number 타입이 아닌, any 타입으로 선언하는 경우 에러가 발생하지 않고 num.length 에 대해 undefined 를 출력하게 됩니다.

 타입스크립트는 any 타입에 대해 타입 검사를 업격하게 하지 않기 때문에 개발자 입장에서 문제가 없는 코드이지만, 실제 애플리케이션에서 문제가 발생할 수 있습니다. 

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

 

 반대로 unknown 타입은 '모른다'의 의미가 강합니다. 할당된 값이 어떤 타입인지 모르기 때문에 함부로 프로퍼티나 연산을 할 수 없습니다.

let num: unknown = 10;
console.log(num.length); // Error: 'unknown' 형식에 'length' 속성이 없습니다.

📌 Null / Undefined Type

 기본적으로 nullundefined 는 다른 모든 타입의 하위 타입임으로, 아무 타입에 할당할 수 있습니다. 

 이때 주의해야할 사항은 tsconfig.json 파일의 컴파일 옵션으로 "strictNullChecks": false 옵션을 설정해주어야 합니다. 

  • strictNullChecks: true
    • 모든 타입은 null, undefined 값을 가질 수 없음. (가지려면 Union type 사용)
    • any 타입은 null, undefined 값을 가질 수 있음.
    • void 타입은 undefined 값을 가질 수 있음.
  • strictNullChecks: false
    • 모든 타입은 null, undefined 값을 가질 수 있음.
//"strictNullChecks": false 옵션 줘야함
let tmp1: number = undefined;
let tmp2: string = null;
let tmp3: { a: 10; b: false } = undefined;
let tmp4: any[] = null;
let tmp5: undefined = null;
let void1: void = null;
let void2: void = undefined;

📌 Void Type

 void 타입은 어떤 타입도 존재할 수 없을 나타냅니다. 보통 반환 값이 없는 함수의 반환 타입을 표현하기 위해 사용됩니다.

 변수를 void 타입으로 선언했을 경우, undefined 값만이 허용됩니다. 이때 strictNullChecks 옵션을 false 로 줬을 경우 null 값도 허용됩니다. 

let void1: void = null; // strictNullChecks: false 일 경우 허용
let void2: void = undefined;

function greeting(): void {
  console.log("hi");
} // 실제로는 undefined 이지만 void 가 아닌 undefined 로 지정하면 에러가 남
const hi = greeting(); // undefined

📌 Never Type

 never 타입은 절대로 발생할 수 없는 타입을 나타낼 때 사용합니다. 어떠한 일이 절대로 일어나지 않을 것이라고 확신할 때 유용합니다. 

 주로 항상 에러를 발생시키거나, 리턴 값을 절대로 내보내지 않는(무한 루프) 함수의 리턴 타입으로 사용됩니다. 

function throwError(): never {
  throw new Error("error");
}
function keepProcessing(): never {
  while (true) {
    console.log("hi");
  }
}

 

 

 

반응형