정적언어. 안정성, 생산성이 높다.
TS가 JS로 변환되는 과정은?
const v1: number | string = 123;
const v2 = () => console.log('123');
// TS -> JS로 변환된 파일
"use strict";
var v1 = 123;
var v2 = function () { return console.log('123'); };
ts.config.json
파일의 compilerOptions
에서 target프로퍼티의 value값을 es5
로 설정 했을 때 저런 식으로 변환이 된다. const size: number = 123;
const isBig: boolean = size >= 100;
const msg: string = isBig ? '크다' : '작다';
// array 타입 소개 방식
const values: number[] = [1, 2, 3];
const values2: Array<number> = [1, 2, 3];
const data: [string, number] = [msg, size]; // 튜플 타입, 각 배열마다 인덱스 지정
data[0].substr(1);
data[1].substr(1); // 타입에러 발생, number에는 substr()이라는 메소드가 없음
console.log('typeof 123 =>', typeof 123); // number
console.log('typeof "abc" =>', typeof 'abc'); // string
console.log('typeof [1,2,3] =>', typeof [1, 2, 3]); // object
Object
로 출력이 된다.let v1: undefined = undefined;
let v2: null = null;
v1 = 123; // Error이유: undefined 타입에 number타입을 지정했기 때문
let v3: number | undefined = undefined; // 유니온 타입
v3 = 123;
console.log('typeof undefined: ', typeof undefined); // undefined
console.log('typeof null: ', typeof null); // object
null은 type으로 존재하지 않고 object로 표현이 된다.
undefined와 null 타입은 다른 타입과 함께 유니온 타입으로 정의할 때 많이 사용된다.
let value: any;
value = 123;
value = '456`;
value = () => {};
any 타입은 모든 종류의 값을 허용한다.
타입 정의가 안된 외부 패키지를 사용하는데 유용하다.
function f1(): void { // 1. 아무것도 반환하지 않으니 void 타입
console.log('hello');
}
function f2(): never { // 2.함수가 항상 비정상적으로 종료되므로 never타입
throw new Error('some error');
}
function f3(): never { // 3. 함수가 종료되지 않으므로 never타입
while(true) {
}
void 타입: 아무 값도 반환하지 않고 종료되는 함수의 반환 타입
never 타입: 예외로 인한 비정상적인 종료나 무한 루프 때문에 종료되지 않는 함수의 반환 타입
let v: object;
v = { name: 'abc' };
console.log(v.prop1); // 타입 에러 발생
객체의 속성에 대한 정보가 없기 때문에 특정 속성값에 접근하면 타입 에러가 발생한다.
속성 정보를 포함해서 타입을 정의하기 위해서는 뒤에 설명할 인터페이스(interface)를 사용해야 한다.
let v1: (1 | 3 | 5) & (3 | 5 | 7);
v1 = 3;
v1 = 1; // 타입 에러
// 변수 v1의 타입은 3 | 5와 같다.
type Width = number | string;
let width: Width;
width = 100;
width = '100px';
enum Fruit {
Apple,
Banana,
Orange,
}
const v1: Fruit = Fruit.Apple;
const v2: Fruit.Apple | Fruit.Banana = Fruit.Banana;
enum Fruit {
Apple, // Q1
Banana = 5,// Q2
Orange, // Q3
}
console.log(Fruit.Apple, Fruit.Banana, Fruit.Orange); // 0, 5, 6
Q1: 열거형 타입의 첫 번째 원소에 값을 할당하지 않으면 자동으로 0이 할당된다.
Q2: 열거형 타입의 각 원소에 숫자 또는 문자열을 할당할 수 있다.
Q3: 명시적으로 값을 입력하지 않으면 이전 원소에서 1만큼 증가된 값이 할당된다.
다른 타입과 달리 열거형 타입은 컴파일 후에도 관련된 코드가 남는다. 예를 들어, TS는 위의 예제 코드를 다음과 같이 컴파일한다.
var Fruit;
(function (Fruit) {
Fruit[Fruit["Apple"] = 0] = "Apple"; // Q2
Fruit[Fruit["Banana"] = 5] = "Banana";
Fruit[Fruit["Orange"] = 6] = "Orange";
})(Fruit || (Fruit = {})); // Q1
console.log(Fruit.Apple, Fruit.Banana, Fruit.Orange); // 0, 5, 6
Q1: 열거형 타입은 객체로 존재한다.
Q2: 열거형 타입의 각 원소는 이름과 값이 양방향으로 매핑된다.
열거형 타입은 객체로 존재하기 때문에 해당 객체를 런타임에 사용할 수 있다. 다음은 열거형 타입의 객체를 사용하는 코드다.
enum Fruit {
Apple,
Banana = 5,
Orange,
}
console.log(Fruit.Banana); // 5
console.log(Fruit.['Banana']) // 5
console.log(Fruit[5]) // Banana // Q1
var Language;
(function (Language) {
Language["Korean"] = "ko";
Language["English"] = "en";
Language["Japanese"] = "jp";
})(Language || (Language = {}));
열거형 타입을 자주 사용한다면 몇 가지 유틸리티 함수를 만들어 사용하는게 좋다.
다음은 특정 열거형 타입에서 원소의 개수를 반환하는 함수다.
function getEnumLength(enumObject: any) {
const keys = Object.keys(enumObject);
// enum의 값이 숫자이면 두 개씩 들어가므로 문자열만 계산한다.
return keys.reduce((acc, key) => (typeof enumObject[key] === 'string' ? acc + 1 : acc), 0);
}
원소가 숫자인 경우에는 양방향으로 매핑되기 때문에 주의해야 한다.
객체의 속성값이 문자열인 경우만 계싼하면 열거형 타입에서 원소의 개수를 구할 수 있다.
function isValidEnumValue(enumObject: any, value: number | string) {
// 값이 숫자이면 양방향으로 매핑했는지 검사한다.
if (typeof value === 'number') return !!enumObject[value];
else {
return Object.keys(enumObject)
.filter((key) => isNaN(Number(key))) // number인 key를 판별해서 filter로 제거한 후
.some((key) => enumObject[key] === value); // 해당 값이 존재하는지 판별한다.
}
}
enum Fruit {
Apple,
Banana,
Orange,
}
enum Language {
Korean = 'ko',
English = 'en',
Japanese = 'jp',
}
console.log(getEnumLength(Fruit), getEnumLength(Language)); // 3, 3
console.log('1 in Fruit', isValidEnumValue(Fruit, 1)); // true
console.log('5 in Fruit', isValidEnumValue(Fruit, 5)); // false
console.log('ko in langue: ', isValidEnumValue(Language, 'ko')); // true
console.log('Korean in Language: ', isValidEnumValue(Language, 'Korean')); // false
isValidEnumValue
함수는 서버로부터 받은 데이터를 검증할 때 유용하게 사용될 수 있다.열거형 타입은 컴파일 후에도 남아있기 때문에 번들 파일의 크기가 불필요하게 커질 수 있다.
열거형 타입의 객체에 접근하지 않는다면 굳이 컴파일 후에 객체로 남겨 놓을 필요는 없다.
상수(const) 열거형 타입을 사용하면 컴파일 결과에 열거형 타입의 객체를 남겨 놓지 않을 수 있다.
const enum Fruit {
Apple,
Banana,
Orange,
}
const enum Language {
Korean = 'ko',
English = 'en',
Japanese = 'jp',
}
const lang: Language = Language.Korean;
const fruit = 0;
const lang = 'ko';
열거형 타입의 객체를 생성하는 코드가 보이지 않는다. 열거형 타입이 사용된 코드는 원소의 값으로 대체되므로 코드가 상당히 간소화된다.
하지만 상수 열거형 타입을 모든 경우에 쓸 수 있는 것은 아니다. 열거형 타입을 상수로 정의하면 열거형 타입의 객체를 사용할 수 없다.
const enum Fruit {
Apple,
Banana,
Orange,
}
function getEnumLength(enumObject: any) {
const keys = Object.keys(enumObject);
// enum의 값이 숫자이면 두 개씩 들어가므로 문자열만 계산한다.
return keys.reduce((acc, key) => (typeof enumObject[key] === 'string' ? acc + 1 : acc), 0);
}
console.log(getEnumLength(Fruit)); // TypeError