[Typescript] 타입(Type)
타입스크립트의 가장 기본이 되는 타입(자료형)들에 대한 정리이다.
얼핏보니, 익숙한 JS 자료형들도 있고, 새로운 자료형과 (파이썬에서 본 튜플같은) 문법들이 등장한다. 차근차근 공부해보았따!
💙 타입 추론(Type Inference)
위 예시를 먼저 보자. ts파일에서, a라는 변수에 5(number)를 할당했다. 그런뒤, 이를 'five'(string) 으로 수정하려하니 에러가 발생했다.
Typescript 에서는 변수타입이 명시되지 않은 경우, 타입정보를 제공하기 위해 유추를 하는데 이것이 '타입 추론' 인 것이다.
다음 예제를 보자. calculateCodingIQ() 라는 함수가 있고, 이는 lostPoints 라는 인자를 받아 뺄셈을 한 값을 반환한다.
Typescript는 이 인자에 대해서도 타입명시를 하지 않았지만, 함수에서 뺄셈을 한다는 사실에 근거하여 number 타입일것임을 추론한다.
💙 타입 명시(Type Notations)
타입 명시는 변수를 선언할 때, 변수값의 타입을 명시함으로써 데이터 형식을 지정해주는 것이다.
const [변수명]: [타입] = [할당값]
const x: string = 'Hello World!';
- 함수의 타입명시
변수와 마찬가지로, 함수에도 타입명시를 적용할 수 있다. 함수의 인자(매개변수) 혹은 결과값에 대해 적용한다.
function getStudentInfo(studentId: number): object {
// 반환값
}
먼저, studentId 라는 함수의 매개변수에 타입을 지정하였다. 또한, 함수 옆에 위처럼 반환값의 타입을 지정할 수 있다.
또한, :any(모든 값), :void(함수가 값을 반환하지 않음) 이외의 타입에서, 해당함수의 return 값이 없다면 에러가 발생한다.
function getStudentInfo(studentId: number): {
studentId: number;
studentName: string;
age: number;
subject: string;
createDate: Date;
} {
// 반환값
}
위 예시는, 앞의 예제함수 반환타입인 객체(object)의 각 필드타입을 세부적으로 명시한 것이다.
Typescript는 이처럼 구체적인 타입정보를 제공할수록 컴파일 타임의 에러를 최소화하고 효율적인 코드가 될 수 있다.
하지만, 이처럼 코드가 복잡해지는 경향이 발생하기 때문에, 여기서 Typescript의 인터페이스(Interface) 문법이 도입된다.
💙 타입 자료형 종류
Typescript는 Javascrip의 슈퍼셋이므로 JS가 지원하는 데이터 타입을 모두 사용가능하며, 추가적인 자료형도 존재한다,
1. 원시 자료형(Primitive Type)
가장 기본적인 자료형태이자, 비원시 자료형(Non-Primitive Type)을 만드는 일종의 재료역할도 한다.
- Boolean : 가장 기본적인 데이터 타입이자, 참/거짓(true/false) 값의 자료형이다.
let isDone: boolean = false;
- Number : 모든 숫자는 부동소수값이다. 특이한 점은, 타입스크립트는 진수에 대해서도 지원을 한다는 점이다.
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
- String : 텍스트 데이터에 해당하는 자료형이다. ""(큰따옴표), ''(작은따옴표) 문자뿐만 아니라, ``(백틱) 문자도 해당된다.
let color: string = "blue";
color = 'red';
let sentence: string = `My favorite color is ${color}`;
- Null, Undefined: null, undefined 를 직접적으로 명시하는 자료형이다. void 타입과 같이, 그 자체로 유용한 경우는 거의 없다.
let u: undefined = undefined;
let n: null = null;
* null, undefined는 다른 타입에도 대입가능한 하위 타입이다. 이를 제한하려면, Config에서 "strictNullChecks": true 를 설정
- Symbol: ES6에 추가된 원시값. Symbol() 생성자로 만들수 있으며, 변경불가(immutable)하며 고유(unique)하다.
// Symbol 예시
let sym1 = Symbol();
let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 == sym3; // false
sym3 === sym3; // true
2. 비원시 자료형(Non-Primitive Type)
- Object : 객체 타입의 자료형. 혹은 내부 필드들의 자료형을 지정할수도 있다. (이는 인터페이스에서 심화되며 추후 다루겠다.)
let Dom: { version: string, el: () => void, css: () => void };
Dom = {
version: '0.0.1',
el(){},
css(){},
}
// 바로 타입 및 변수선언
let Dom: { version: string, el: () => void, css: () => void } = {
version: '0.0.1',
el(){},
css(){},
}
이 객체에, JS와 마찬가지로 속성을 추가하려고 한다. 이 때, 타입명시가 없다면 에러가 발생한다.
Dom.each = function(){}; // 'each' 속성이 없습니다.
그렇기에, 초기 타입선언에서 추가속성에 대한 타입을 설정할 수 있다.
let Dom: {
// ...
[propName: string]: any
}
- Array : JS처럼 특정 타입의 요소들에 대한 배열을 타입으로 지정할 수 있다. 문법은 2가지가 있다. - 1) type[], 2) generic
// 1) element type + []
let list: number[] = [1, 2, 3];
// 2) generic type
let list: Array<number> = [1, 2, 3];
- Tuple : 튜플 타입이며 Javascript에선 지원하지 않는 타입이다. 요소의 타입과 개수가 고정된 배열을 선언할 수 있다.
let x: [string, number];
x = ["hello", 10]; // 성공
x = [10, "hello"]; // 오류
- Enum : 열거 타입이며 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다. 중괄호로 나열하며, 각 멤버엔 번호나 이름으로 접근.
enum Color {Red, Green, Blue}
let c:number = Color.Red // 0
let c:string = Color[1] // 'Green'
Enum은 순서값을 임의로 지정할 수도 있다. 지정되지 않았다면, 앞 순서값의 +1로 자동으로 설정된다.
enum Color {Red = 1, Green = 2, Blue} // Blue = 3
let c: Color = Color.Green;
- Any : Typescript는 명시적으로 데이터 타입을 지정하는것을 지향한다. 하지만, 이것이 불분명할 경우 any로 모든 타입할당이 가능하다.
let product_id:any = 124981;
product_id = 'p9023412'; // any 유형이 설정되었으므로 어떤 유형도 값으로 할당 가능
// 암시적으로 any 타입 지정
let product_id;
product_id = 124981;
product_id = 'p9023412';
- Function / Union / Void
1) 함수 매개변수 타입 : 매개변수(인자)는 기본적으로 any 타입 기반이다. 이들 역시 일종의 변수로, 각각 타입을 지정할 수 있다.
function setInfo(id:number, name:string) {
return { id, name };
}
let product_one = setInfo(120912, '스노우보드');
2) 유니온 타입 : 매개변수에 복수의 타입을 적용하고자 할 때 문법이다. 파이프(|) 를 통해 설정한다.
function setInfo(id: number|string, name: string) {
return { id, name };
}
3) 함수 반환값 타입 : 반환값의 타입을 지정할 수 있다. void는 결과값을 반환하지 않는 함수에 설정한다.
// 리턴 값 타입이 명시적으로 설정되지 않는 함수
function assignClass(name:string): void {
document.documentElement.classList.add(name);
}
// 리턴 값 타입이 숫자인 함수
function factorial(n:number): number {
if (n < 0) { return 0; }
if (n === 1) { return 1; }
return n * factorial(n-1);
}
// 리턴 값 타입이 문자인 경우
function repeat(text:string, count:number=1): string {
let result:string = '';
while(count--) { result += text; }
return result;
}
4) 함수 식 타입지정 : 함수의 변수에 형식을 지정하거나, 혹은 직접 적용할 수 있다.
// 1) 변수에 함수 매개변수, 리턴 타입에 대한 명시적 설정
let assignClass: (n:string) => void;
assignClass = function(name) {
document.documentElement.classList.add(name);
};
// 2) 함수 자체에 직접 타입 기술
let factorial:(n:number)=>number = n => {
if (n < 0) { return 0; }
if (n === 1) { return 1; }
return n * factorial(n-1);
};
- Never : 절대 발생할 수 없는 타입을 나타낸다. 일반적으로 함수의 리턴 타입으로 사용된다.
never 타입의 함수는 항상 오류를 출력하거나 리턴값을 절대 반환하지 않음을 의미한다. (일종의 무한 Loop)
// 항상 오류 발생
function invalid(message:string): never {
throw new Error(message);
}
// never 타입을 결과 추론(Inferred)
function fail() {
return invalid('실패');
}
// 무한 루프
function infiniteAnimate(): never {
while ( true ) { infiniteAnimate(); }
}
3. 사용자 정의 타입(Type Aliase)
위 객체같은 예시에서, 우리는 복잡한 타입을 매번 정의하기가 번거롭다. 이러한 타입 재사용을 위한 기능이 타입 별칭(Type Aliase) 다.
// 타입 별칭(Type Alias)
type operation = {
data: number[],
output:(num:number)=>number[]
};
// 사용자 정의 타입 operation 적용 예시
let sum:operation = {
data: [10, 30, 60],
output(num){
return this.data.map(n=>n+num);
}
};
let multiply:operation = {
data: [110, 230, 870, 231],
output(num){
return this.data.map(n=>n*num);
}
};
먼저, operation 이라는 변수에 Type Alias 처리를 한다. 이 때 쓰이는 키워드는 type 이다.
이제, operation은 타입명이자 특정한 객체(안에는 배열과 함수를 각각 필드로) 자료형을 의미한다. (새로운 타입이 아닌 참조타입)
이를, 다른 변수(sum, multiply)에 동일하게 적용할 수 있다.
* Type Aliase vs Interface
타입을 커스텀하기 위해서, Type Aliase 방법도 있지만 이후 공부할 Interface 방법도 있다고 했다. 이들은 차이점이 있다.
- 인터페이스는 정의한 타입의 추가가 가능하나, 타입 엘리어스는 불가능하다. (초기설정을 유지)
- 인터페이스는 extends, implements 가 가능하지만, 타입 엘리어스는 불가능하다. (2.7 버전부터 intersection type(&) 로 상속가능)
* 참고링크: www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases
4. 고급 자료형
다양하고 복합적인 타입 형태들이 존재한다. 고급 자료형의 존재와 종류만 알고 자세한 부분은 핸드북을 참고해달라.
(Typescript 핸드북 링크: typescript-kr.github.io/pages/advanced-types.html )
- Intersection(교차 타입)
- 여러 자료형을 하나로 만들어줍니다.
- 따라서, 해당 자료형을 가진 변수는 재료가 되는 자료형들의 모든 멤버와 메소드를 가집니다.
- Union(유니온 타입)
- 여러 자료형 중의 하나가 될 수 있음을 의미합니다.
- 즉, 컴파일러 입장에서는 재료가 되는 자료형들 중 어떤 자료형이 될 지 예측할 수 없습니다.
- 따라서, 해당 자료형을 가진 변수는 재료가 되는 모든 자료형들의 공통된 멤버나 메소드만 가질 수 있습니다.
- Nullable(널러블)
- String Literal(문자열 리터럴)
- Numeric Literal(숫자 리터럴)
- Enum Member(열거형 멤버)
- Discriminated Unions(판별 유니온 -> 엄격검사)
- Polymorphic this(다형성 this)
- Index(인덱스)
- Mapped(맵핑)
- Conditional(조건부)
💙 Type Assertion
해당 변수의 타입을 Compiler 에게 알려주는 것이다. 타언어의 타입 캐스트(Cast)와 유사하나, 검사나 데이터 재구성이 발생되지 않는다.
이는, 개발자가 타입을 인지했음을 Compiler에 알리는 개념으로, 런타임 시에는 영향을 미치지 않으며 컴파일 타임에만 영향을 미친다.
타입 어설션은 2가지 문법이 있다. 1) angle-bracket, 2) as
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;
* 두 방법 모두 결과는 동일하다. 하지만, JSX와 함께 사용하는 경우는 as 문법만 허용된다!
타입만 해도 공부할 게 정말 많았으며, 특히 튜플이나 열거는 앞으로 어떤 경우에 유용하게 활용될 지 의문이다.
다음은, 중간중간 언급한 타입의 커스텀을 위한 인터페이스(Type Aliase 와는 또다른) 에 대해 공부하겠다.
[출처]
- Typescript 핸드북 : typescript-kr.github.io/pages/basic-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8-type-assertions
- Typescript 가이드북 : yamoo9.gitbook.io/typescript/types/type-assertions
- kjwsx23 님의 블로그 : kjwsx23.tistory.com/450?category=746259