Core Types
Type | Example | Description |
number | 1, 5.3, -10, -2,3, ... | 정수형과 실수형을 포함한 모든 숫자 |
string | 'Hi', "Hi", `Hi` | 모든 문자값 |
boolean | true, false | '참'이나 '거짓'을 나타내는 단 두가지의 값 |
object | { age: 30 } | 기존 JS의 object와 더 구체적인 타입도 가능 |
Array | [1, 2, 3] | 어떤 JS 배열이든 지원하며 배열의 타입을 유연하게 또는 제한적으로도 지정 가능 |
Tuple | [1, 2] | TS에만 있는 길이와 타입이 모두 고정된 배열 |
Enum | enum { NEW, OLD } | TS에만 있는 열거형 전역 상수 식별자 |
Any | * | 구체적인 타입이 지정되있지 않은 아무 타입 |
Example
/* ts */
function add(n1, n2) {
return n1 + n2;
}
const n1 = 5;
const n2 = 2.8;
const result = add(n1, n2);
console.log(result);
/* js */
function add(n1, n2) {
return n1 + n2;
}
var n1 = 5;
var n2 = 2.8;
var result = add(n1, n2);
console.log(result);
=> 7.8을 출력하게 된다.
/* ts */
function add(n1, n2) {
return n1 + n2;
}
const n1 = '5';
const n2 = 2.8;
const result = add(n1, n2);
console.log(result);
/* js */
function add(n1, n2) {
return n1 + n2;
}
var n1 = '5';
var n2 = 2.8;
var result = add(n1, n2);
console.log(result);
=> 문자열을 연결한 값이 되므로 '52.8'을 출력하게 된다.
/* ts */
function add(n1: number, n2: number) {
return n1 + n2;
}
const n1 = '5';
const n2 = 2.8;
const result = add(n1, n2);
console.log(result);
=> ts에서는 위와 같이 할당하려는 값 옆에 ':'를 붙여 타입과 같이 선언할 수 있다.
따라서 위의 코드는 n1: number 인데, 문자열 '5'를 할당했으므로 에러가 나게된다.
TS vs. JS
- JS
- 동적 타입이다. 숫자형으로 선언한 변수에 나중에 문자열을 할당해도 아무이상이 없다.
- 따라서 런타임시 특정 타입만을 사용해야 할 때 typeof 연산자를 통해 확인해야 한다.
- TS
- 정적 타입이다. 개발 도중에 변수와 매개변수의 타입을 정의한다.
- 따라서 런타임시에 갑자기 변경되지 않는다.
=> 따라서 TS를 사용하는 것이 타입을 확실히 할 때 더 좋다.
하지만 이런 TS 기능과 타입 체크 기능이 JS 엔진에 내장되어 있지 않기 때문에 TS를 사용하면 개발 도중에만 지원을 받을 수 있다. 그러므로 브라우저에서는 실행할 수 없다.
number, string, boolean
/* ts */
function add(n1: number, n2: number, showResult: boolean, phrase: string) {
const result = n1 + n2;
if(showResult) {
console.log(phrase + result);
}
else {
return result;
}
}
const n1 = 5;
const n2 = 2.8;
const printResult = true;
const resultPhrase = 'Result is : ';
add(n1, n2, printResult, resultPhrase);
타입 할당 및 타입 추론
위의 코드에서 보면 함수의 매개변수에서는 타입을 설정해주었지만, 함수 바깥에서 함수에 전달할 값을 초기화 할 때나, 함수 안에서 n1과 n2를 계산할 때는 JS와 같이 따로 타입을 설정해주지 않았다.
그럼에도 n1과 n2는 number로 인식되고, 다른 타입들도 우리가 설정한 타입과 똑같이 인식이 되었다.
이것이 가능한 이유는 TS가 타입 추론을 완벽하게 해주기 때문이다.
물론 const n1: number = 5
와 같이 선언할 때 타입을 지정해줄수도 있다.
하지만 TS가 타입 추론을 완벽히 해내기 때문에 이 작업은 중복 작업이 되며, 굳이 할 필요가 없다.
그런데 변수를 선언했지만 값을 할당하지 않았을 경우에는 나중에 이 변수에 나중에 어떤 타입을 저장할 지 TS에 알려주는 것이 좋다. let n1: number
와 같이 말이다. 물론 굳이 지정을 해주지 않아도 괜찮다. 하지만 나중에 문자열을 저장하게 될 경우 에러가 생기지 않는다. 그러므로 알려주는 것이 좋다.
객체 형태
const person = {
name: 'Maximilian',
age: 30
};
console.log(person.gender);
만약 위 코드가 JS라면 에러 없이 person이라는 객체에 gender라는 키값은 없으므로 undefined를 출력하게 될 것이다.
하지만 TS라면 컴파일 시에 객체에 없는 키 값에 접근할 경우에는 에러가 발생한다.
배열 타입
/* ts */
const person = {
name: 'Maximilian',
age: 30,
hobbies: ['Sports', 'Cooking']
};
let favoriteActivities: string[];
favoriteActivities = ['Sports'];
console.log(person.name);
for(const hobby of person.hobbies) {
console.log(hobby.toUpperCase());
}
위의 코드에서 마지막 반복문의 hobby에 toUpperCase()를 사용할 수 있다. 이 말은 hobby가 문자열로 인식된다는 말이다.
반복문에서 쓰이는 hobby는 따로 타입을 지정해주지 않았지만 객체에서의 hobbies가 string[]로 타입 추론이 되었다.
그러면 자동으로 그 안에 있는 것들은 string 타입으로 타입 추론이 되는것이다.
튜플
/* ts */
const person: {
name: string;
age: number;
hobbies: string[];
role: [number, string];
} = {
name: 'Maximilian',
age: 30,
hobbies: ['Sports', 'Cooking'],
role: [2, 'author']
};
person.role = [0, 'admin', 'user'] // 2개만 들어갈 수 있으므로 error
person.role.push('admin'); // push는 예외적으로 가능하다
person.role[1] = 10; // 문자열 자리에 숫자를 넣으므로 error
let favoriteActivities: string[];
favoriteActivities = ['Sports'];
console.log(person.name);
for(const hobby of person.hobbies) {
console.log(hobby.toUpperCase());
}
배열에 정확히 n개의 값이 필요하고 각 값의 타입을 미리 알고 있는 상황에서는 배열보다는 튜플을 사용하여 작업 중인 데이터 타입과 예상되는 데이터 타입을 보다 명확하게 파악할 수 있다.
열거형
/* ts */
enum Role { ADMIN, READ_ONLY, AUTHOR };
const person = {
name: 'Maximilian',
age: 30,
hobbies: ['Sports', 'Cooking'],
role: Role.ADMIN
};
if(person.role === Role.ADMIN) {
console.log('is admin');
}
Role의 ADMIN, READ_ONLY, AUTHOR은 각각 0, 1, 2 의 값을 갖는다.
물론 각각 다른 값을 지정해줄수도 있으며, 만약 맨 앞의 ADMIN에만 5로 지정해줬을경우 그 뒤의 두 값은 5부터 1씩 더해진 6과 7의 값을 갖는다. 숫자 뿐만 아니라 문자열도 지정이 가능하다!
/* JS */
var Role;
(function (Role) {
Role[Role["ADMIN"] = 0] = "ADMIN";
Role[Role["READ_ONLY"] = 1] = "READ_ONLY";
Role[Role["AUTHOR"] = 2] = "AUTHOR";
})(Role || (Role = {}));
;
var person = {
name: 'Maximilian',
age: 30,
hobbies: ['Sports', 'Cooking'],
role: Role.ADMIN
};
if (person.role === Role.ADMIN) {
console.log('is admin');
}
위 코드는 컴파일된 JS 파일이다.
Any
any 타입이 지정된 곳에는 TS 컴파일러가 작동을 하지 않게 된다. 어떠한 타입이라도 올 수 있기 때문에 필요가 없기 때문이다. 따라서 정말 특정한 상황이 아니고서야 가급적 사용하지 않는 것이 좋다.
조합
/* ts */
function combine(input1: number | string, input2: number | string) {
let result;
if(typeof input1 === 'number' && typeof input2 === 'number') {
result = input1 + input2;
}
else {
result = input1.toString() + input2.toString();
}
return result;
}
const combinedAges = combine(30, 26);
console.log(combinedAges);
const combinedNames = combine('Max', 'Anna');
console.log(combinedNames);
큰 틀로 보아 같은 함수를 사용해야 하는데 들어오는 타입이 다를 수도 있다면 위와 같이 조합해서 사용할 수 있다.
물론 위와 같은 경우, 그리고 대부분에 union type을 사용하게 될 경우 타입 검사를 거쳐서 각 타입마다 다른 로직을 적용해주어야 한다.
리터럴 타입
/* ts */
function combine(
input1: number | string,
input2: number | string,
resultConversation: 'as-number' | 'as-text'
) {
let result;
if (
(typeof input1 === 'number' && typeof input2 === 'number') ||
resultConversation === 'as-text'
) {
result = +input1 + +input2;
} else {
result = input1.toString() + input2.toString();
}
return result;
}
const combinedAges = combine(30, 26, 'as-number');
console.log(combinedAges);
const combinedStringAges = combine('30', '26', 'as-number');
console.log(combinedStringAges);
const combinedNames = combine('Max', 'Anna', 'as-text');
console.log(combinedNames);
정해져 있는 리터럴 값 중에 선택해야 할 경우, 타입 대신 정의된 리터럴로 선언할 수 있다.
타입 알리어스 / 사용자 정의 타입
/* ts */
type Combinable = number | string;
type ConversionDescriptor = 'as-number' | 'as-text';
function combine(
input1: Combinable,
input2: Combinable,
resultConversation: ConversionDescriptor
) {
let result;
if (
(typeof input1 === 'number' && typeof input2 === 'number') ||
resultConversation === 'as-text'
) {
result = +input1 + +input2;
} else {
result = input1.toString() + input2.toString();
}
return result;
}
const combinedAges = combine(30, 26, 'as-number');
console.log(combinedAges);
const combinedStringAges = combine('30', '26', 'as-number');
console.log(combinedStringAges);
const combinedNames = combine('Max', 'Anna', 'as-text');
console.log(combinedNames);
중복되는 타입을 type 키워드를 통해 미리 선언함으로써 가져다 쓸 수 있다.
< 2023 / 01 / 07 추가 >
함수 반환 타입 및 'void'
/* ts */
function add(n1: number, n2: number) {
return n1 + n2;
}
function printResult(num: number): void {
console.log('Result: ' + num);
}
printResult(add(5, 12)); // 17
console.log(printResult(add(5, 12))); // undefined
TS에는 JS에 존재하지 않는 void 타입이 있다. TS 에서는 변수의 타입 만큼 중요한 것이 함수의 리턴 타입이다. 위와 같이 명시적으로 함수 옆에 타입을 붙여줄 수 있지만 보통의 경우에는 TS가 타입 추론을 하도록 하는것이 좋다.
void는 함수가 아무것도 반환하지 않음을 나타내는데, 출력해보면 undefined가 나온다.
undefined도 TS에서는 하나의 타입이다. 따라서 함수의 리턴 타입으로 undefined를 지정해주면 존재하지 않는 것을 return 하는 것을 나타내야 하므로, return;
와 같이 함수 마지막에 넣어주어야 한다.
타입의 기능을 하는 함수
function add(n1: number, n2: number) {
return n1 + n2;
}
function printResult(num: number): void {
console.log('Result: ' + num);
}
printResult(add(5, 12)); // 17
console.log(printResult(add(5, 12))); // undefined
let combinedValues: (a: number, b: number) => number;
combinedValues = add;
console.log(combinedValues(8, 8));
위와 같이 변수에 함수 자체를 할당해줄 수 있다. combinedValues는 매개변수로 number 형을 가지는 두 매개변수를 받고, 리턴타입이 number인 함수만 넣어줄 수 있다. 따라서 add 대신 printResult를 할당하게 되면 에러가 발생한다.
함수 타입 및 콜백
function addAndHandle(n1: number, n2: number, cb: (num: number) => void) {
const result = n1 + n2;
cb(result);
}
addAndHandle(10, 20, (result) => {
console.log(result);
});
위의 코드에서 addAndHandle의 마지막 인자로 들어온 callback 함수는 number로 따로 타입을 지정해주지 않아도 자동으로 number라고 추론하게 된다. 그 이유는 함수의 매개변수를 선언할 때 num을 number 타입이라고 지정해주었기 때문이다. 또한 리턴타입을 void라고 지정해주었는데, 따라서 callback 함수에서 어떠한 값을 return 하게 되더라도 return 값을 무시하게 된다.
'unknown' 타입
let userInput: unknown;
let userName: string;
userInput = 5;
userInput = 'Max';
if(typeof userInput === 'string') {
userName = userInput;
}
위의 코드는 에러가 나지 않는다. unknown은 말 그대로 어떠한 타입의 값이 들어올지 알 수 없을 때 사용하는 것인데, 만약 위 코드에서 unknown 대신 any를 사용하게 된다면 에러가 날 것이다. 이미 number 형 변수를 받았는데, string 으로 재할당을 하려고 했기 때문이다.
그리고 string 형에 unknown 타입을 넣을 수 없다. 하지만 위와 같이 if 문을 통해 타입을 확실히 판단하고 나면 할당이 가능해진다. 이것이 any 타입보다 unknown 타입이 나은 경우이다. 하지만 왠만한 경우는 사용하지 않고, 미리 타입을 알 수 있다면 그 타입으로 선언을 하거나, 유니언 타입을 사용하는 것이 낫다.
'Web > TS' 카테고리의 다른 글
TS Compiler (0) | 2023.01.10 |
---|