TS
의 자료형(Type)에 대해 알아보도록 하자.
Primitive Type의 종류는 아래와 같다.
변수에 Primitive Type을 지정해주도록 하겠다. 콜론(:)으로 type을 표시해 주도록 한다.
변수에 type을 명시해준 뒤 값을 할당하거나, 변수와 type 명시하며 바로 값을 할당해줄 수도 있다.
let age: number;
age = 12;
// 또는
let age: number = 12;
위처럼 number
type을 명시한다면 숫자(실수(float) 포함) 외의 값을 할당할 경우 에러가 발생한다.
다른 Primitive Type(string, bolean)도 사용법은 동일하다. 유의할 점은 type의 표기가 소문자로 시작되어야 한다는 것이다.
Number
처럼 대문자 시작으로 쓰더라도 에러는 발생하지 않지만, 이 경우는 JS
의 Number
객체를 가리키게 된다.
예제는 변수에 type을 지정해보았다. 지난 글의 예제와 같이 매개변수에도 똑같이 type 지정이 가능하다. 변수, 매개변수 외의 영역에 type 지정을 하는 것도 차차 알아보도록 하겠다.
그리고 number
, string
, boolean
type 외에 null
과 undefined
도 있지만 자주 사용되지는 않는다. 무언가를 null
이라고 지정하는 경우는 거의 없기 때문이다.
let hobbies: null;
// 에러
hobbies = 12;
위처럼 null
로 type 지정은 가능하나 막상 특정 값을 할당하려 하면 에러가 발생한다. 그렇기때문에 null
과 undefined
는 조금 다른 방식으로 사용된다.
차차 알아보도록 하고, 지금으로썬 number
, string
, boolean
이 자주 사용되는 주요 Primitive Type이라는 것만 기억해두도록 하자.
Array와 object Type의 기본 사용법도 Primitive Type과 동일하다. 콜론(:)으로 type 지정을 해주면 된다.
먼저 array type을 예제를 통해 사용해보도록 하자. array이기 때문에 []
로 type을 명시해준다.
유의해야 할 것은 []
안의 내용 값이 어떤 type인지도 함께 명시해주어야 한다는 점이다.
// Array type 지정
let hobbies: string[];
hobbies = ['Sports', 'Cooking'];
// 아래의 경우 에러 발생
hobbies = ['Sports', 'Cooking', 12];
이번엔 object type 예제를 살펴보도록 하자.
// Object type 지정
let person: {
name: string;
age: number;
};
person
이라는 object를 선언하며, 그 안에는 string
type인 name
과 age
값이 있음을 나타내고 있다.
위의 array 예제에서 []
안에 어떠한 type의 값이 들어가있는지 명시해준 것과 같이, {}
안의 변수는 어떠한 type을 가진 값인지 각각 지정해주고 있다.
// OK
let person: {
name: 'Max',
age: 12,
};
// Not OK
let person: {
isEmolyee: true
};
위의 예제처럼 object가 지정된 type외의 값을 가지고 있다면 에러가 발생한다.
array와 object를 함께 명시할 수도 있다.
let people: {
name: string;
age: number;
}[];
위의 예제는 people
에 객체 배열을 저장하겠다는 것을 나타낸다.
Type 추론이란 무엇일까? 예제를 통해 알아보도록 하자.
// 에러 발생
let course = 'React and Typescript';
course = 12345;
위처럼 코드를 작성하면 에러가 발생한다. 별다른 type 지정도 하지 않았는데, 왜 에러가 나는 것일까?
TS
의 핵심 기능인 type 추론 때문이다.
기본적으로 TS
는 type이 표기되어 있지 않더라도 어떤 type을 어디에 사용해야 할지 추론한다.
그렇기 때문에 위의 예제에서 course
는 string
이란 type으로 추론되어 12345
라는 값을 받을 수 없다. 변수명에 커서를 올려보면 TS
가 어떤 type으로 추론하고 있는지 확인할 수 있다.
물론 지금까지의 예제처럼 직접 하나하나 type을 지정해 줄 수도 있다. (굳이..?)
하지만 type 추론 기능을 활용하여 코드를 작성하는 게 권장되는 방식이다. 작성할 코드가 줄어들기 때문이다.
지금까지 알아본 예제의 변수들은 number
, string
과 같이 하나의 type을 가지고 있었다. 대부분 이렇게 사용하게 되지만 종종 여러 type을 지정해야 할 경우도 생긴다.
예를 들어보도록 하자.
let course = 'React and Typescript';
course = 12345;
course
가 'React and Typescript'라는 값(string
)도 받고, 12345라는 값(number
)도 받게 하려면 어떻게 해야 할까?
이렇게 여러 type 지정을 해주는 것이 union type이다. 사용법은 아래와 같다.
let course: string|number = 'React and Typescript';
course = 12345;
위와 같이 지정해주면 에러가 뜨지 않는다. 이제는 course
에 string
과 number
를 모두 저장할 수 있기 때문이다.
이처럼 Union type은 TS
의 핵심 기능 중 하나로 type과 값을 좀 더 유연하게 정의할 수 있도록 도와준다.
작업을 하다보면, 어느 시점부터 동일한 type을 반복하여 정의하고 있는 것 같은 느낌이 든다.
동일한 type을 정의해도 문제될 건 없지만, 이러한 코드 중복을 피하기 위해 사용하는 것이 Type Alias(별칭)이다.
간단하게 말하자면 직접 base type을 만들어 놓고, 반복된 코드를 적는 대신 해당 type alias를 사용하는 것이다.
예를 들어보도록 하자.
Type Alias(별칭)는 type
키워드와 함께 정의내릴 수 있다.
// type alias 미사용
let person: {
name: string;
age: number;
};
// type alias 사용
type Person = {
name: string;
age: number;
}
let Person: Person;
let people: Person[];
주의해야할 점은 type alias 사용 시, 콤마(:)가 아닌=
로 해당 값의 type을 정해주어야 한다는 것이다. type alias는 TS
에만 존재하는 기능이기 때문에 JS
로 컴파일시 코드에서 삭제되기 때문이다.
그러므로 =
오른 쪽에 있는 것은 JS
값이 아니라 type을 정의한 내용을 나타낸다.
type alias로 Person
이라는 별칭의 객체 값에 기본 type을 지정해주었다. 이후 필요한 모든 곳에서 반복 사용이 가능하다.
함수를 사용할 때는 type을 지정하는 위치가 따로 있다.
예제와 함께 알아보도록 하자.
function add(a: number, b: number) {
return a + b;
}
위와 같은 코드 작성시, type 추론이 되는 곳이 있다.
TS
는 함수의 return
값의 type을 통해 함수 type을 추론한다.
함수 이름 add
에 커서를 올려보면 아래와 같이 확인이 가능하다.
함수의 type이 number
라고 추론되어 있다.
물론 아래와 같이 우리가 직접 함수의 type을 지정할 수도 있다.
하지만 추론 부분에서 언급했던 바와 같이, 특별한 이유가 없다면 자동적으로 TS
가 추론하도록 하는 것이 좋다.
함수 type에서 유념해두어야 할 점은 매개변수의 type뿐만 아니라 return
값의 type도 생각해야 한다는 것이다.
그리고 이 return
값에는 특별한 type이 있다. 바로 void
type이다.
function printOutput(value: any) {
console.log(value);
}
위의 함수는 아무것도 반환하지 않았다. return
문이 없다.
이런 경우 가지게 되는 특별한 반환 type이 void
이다. 해당 함수에 반환 값이 없다는 것을 뜻한다.
void
는 null
, undefined
와 비슷하지만 항상 함수와 결합하여 사용한다는 특징이 있다.
만약 위 예제 함수의 반환값을 받아 작업하려면 undefined
type으로 값을 받아와야 한다.
**Generic을 자세히 알아보기 앞서 예제를 들어보자.
function insertAtBeginning(array: any[], value: any) {
const newArray = [value, ...array];
return newArray;
}
const demoArray = [1, 2, 3];
const updatedArray = insertAtBeginning(demoArray, -1); // [-1, 1, 2, 3]
insertAtBeginning
함수에 어떤 type의 매개변수가 올지 확신할 수 없기에 type을 any
로 지정해보자.
나머지 코드 작성을 마치고 updatedArray
에 커서를 올려보면, 실제 들어있는 값은 [-1, 1, 2, 3]으로 number[]
와 같이 추론되어야 하지만 TS
는 updatedArray
가 any[]
라고 나타내고 있음을 확인할 수 있다. 애초에 array: any[], value: any
라고 설정해주었기 때문이다.
위의 상황에서 아래와 같이 코드를 작성해보자.
function insertAtBeginning(array: any[], value: any) {
const newArray = [value, ...array];
return newArray;
}
const demoArray = [1, 2, 3];
const updatedArray = insertAtBeginning(demoArray, -1); // [-1, 1, 2, 3]
updatedArray[0].split('');
딱히 화면상으론 에러 표시가 없지만 막상 실행해보면 런타임 에러가 발생한다. TS
는 해당 코드의 type에 문제가 없다고 판단하지만 updatedArray
의 number
값으로 string
객체에만 호출 가능한 split()
은 사용할 수 없기 때문이다.
이와 같이 TS
가 올바른 추론을 하지 못하는 경우는 어떻게 해야할까?
바로 이런 상황에서 우리는 generic을 사용한다.
generic 기능을 사용하여 insertAtBeginning
함수를 generic 함수로 변환해 보도록 하자.
함수 이름과 매개변수 사이에 <>
를 넣어주고, 이곳에 generic type을 표기해 주자. 일반적으로 Type의 'T'를 따와 표기하지만, 어떤 식별자를 사용해도 상관은 없다.
그리고 any
type 대신 generic type을 매개변수에도 표기해 준다.
function insertAtBeginning<T>(array: T[], value: T) {
const newArray = [value, ...array];
return newArray;
}
const demoArray = [1, 2, 3];
const updatedArray = insertAtBeginning(demoArray, -1); // [-1, 1, 2, 3]
updatedArray[0].split('');
이제 TS
는 insertAtBeginning
함수에 들어온 인자를 정확하게 살펴보게 된다. generic type을 통해 insertAtBeginning
의 매개변수 type이 any
가 아닌, 같은 T라는 type의 무언가라는 정보를 알려주었기 때문이다.
그러므로 TS
는 insertAtBeginning
에 들어가는 demoArray
, -1
의 type을 먼저 살피고 최종적으로 updatedArray
의 type을 number[]
로 추론하게 된다.
그러므로 updatedArray[0].split('')
은 에러가 발생하게 된다.
이렇게 insertAtBeginning
함수에 generic type을 지정하였기 때문에 아래와 같은 사용도 가능하다.
function insertAtBeginning<T>(array: T[], value: T) {
const newArray = [value, ...array];
return newArray;
}
const demoArray = [1, 2, 3];
const updatedArray = insertAtBeginning(demoArray, -1); // [-1, 1, 2, 3]
const stringArray = insertAtBeginning(['a', 'b'], 'c');
해당 예제에서 generic은 함수에 type 안정성과 유연성을 주었다.
어떤 type이든 사용 가능하며, 또 특정 type을 사용해 함수를 실행하고 나면 해당 type으로 고정시켜주어 동작하도록 도와준다.