TypeScript?
JavaScript는 타입을 검증하는 언어가 아니며, ES6이전에 JavaScript는 클래스라는 개념을 직접적으로 제공하지 않았다. ES5, 6 등의 JavaScript Spec이 나오면서 class, import 등의 새로운 키워드가 생기며 클래스와 모듈화를 위한 문법을 정으되었지만 아직 모든 web browser에서 지원하는 것이 아니기 때문에 마음대로 사용하기에는 아직 문제가 많다. 이를 위해 Babel과 같은 Transpiler 같은 것들이 등장했지만, 타입을 엄밀히 검증해주길 바라며 interface를 그리워하는 사람들에게는 여전히 불만이 남아있었다.
TypeScript는 Microsoft에서 개발한 JavaScript를 한번더 랩핑한 언어이다. 전체적으로 JavaScript와 비슷하지만 가장 큰 차이점으로 타입을 정의하고 검증을 수행한다. 아래의 코드와 같이 변수에 타입을 명시할 수 있으며, 예전에 JavaScript를 사용할때 함수에 원하지 않는 유형의 값이 들어가는 불미스러운 사건을 사전에 방지할 수 있다. 또한 TypeScript는 ES6를 포함하기 때문에 기존의 JavaScript 코드도 그대로 사용할 수 있으며, interface도 제공하기 때문에 보다 구조화된 개발을 할 수 있다. ES6와 typesafe하다는 것을 제외하더라도 TypeScript는 여러 편의기능 및 문법을 제공한다.
TypeScript 예제
// example1.ts
interface Person {
getName(): string;
getAge(): number;
}
class Student implements Person {
firstName: string;
lastName: string;
age: number;
constructor(firstName: string, lastName: string, age: number) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
getName(): string {
return `${this.firstName} ${this.lastName}`;
}
getAge(): number {
return this.age;
}
}
TypeScript 설치
Node Package Manager를 사용한다면 매우 쉽게 설치할 수 있다.
npm install -g typescript
TypeScript 컴파일
TypeScript 패키지는 자체적으로 매우 우용한 도구를 많이 제공한다. 그 중 가장 기본이 되는 도구는 TypeScript를 JavaScript 코드로 변환시켜주는 tsc
이다. tsc
를 아래의 예제와 같이 사용하면 같은 위치에 JavaScript코드가 생성된다. 기타 다른 우용한 도구들은 다음에 알아보도록 하겠다.
tcs example1.ts
Editor
MS에서 만든 언어인 만큼 Visual Studio에서도 역시 TypeScript를 지원한다. 그러나 Visual Studio를 누구나 사용할 수 있는 것은 아니기에 다른 프로그램을 찾는 사람도 많을 것이다. 다행이 다른 여러 개발도구들이 TypeScript의 플러그인을 제공한다. 예를 들어, Atom도 TypeScript를 위한 플러그인을 제공하며, 플러그인을 이용하면 매우 편하게 개발을 할 수 있다.
Getting Started
Type
String
모든 언어에서 볼 수 있는 바로 그 문자열 타입이다. 타입의 이름은 string
이며 JavaScript처럼 큰따옵표(“) 또는 작은따옴표(‘)로 감싸서 표현을 한다. 그런데 TypeScript에는 문자열을 표현하는 방법아 한가지 더 있다. 바로 억음부호(`)인데 이것으로 감싸서 문자열을 표현하면 내부적으로 ${...}
와 같은 표현식을 사용해서 변수값을 편하게 가져다 쓸 수 있다.
let firstName: string = 'Kw'; // 작은따옴표
let lastName: string = "Seo"; // 큰 따옴표
console.log(`firstName: ${firstName} and lastName: ${lastName}`) // outputs: 'firstName: Kw and lastName: Seo'
// 위를 JavaScript 코드로 표현하면 아래처럼 될 것이다.
console.log('firstName: ' + firstName + ' and lastName: ' + lastName);
Number
TypeScript는 따로 정수형이나 실수형을 구분하지 않고 number
라는 타입 하나만 존재한다. number
는 실수이다.
function sum(a: number, b: number): number {
return a + b;
}
Boolean
true나 false같은 논리값을 표현하는데 사용된다. 타입명은 boolean
이다.
Array
JavaScript에서 편하게 많이 이용되던 Array 역시 타입으로써 명시할 수 있다. 방법에는 두 가지가 있는데 아래와 같다.
let list: number[] = [1, 2, 3];
let list; Array<number> = [1, 2, 3];
Tuple
TypeScript에서 Tuple은 Array 같이 표현된다. 단, element의 수가 정해져있어야 한다. 수가 정해진 Array에 각 element 타입을 명시하는 것으로 Tuple을 표현할 수 있다.
let x: [string, number];
x = ["hello", 10]; // ok
x = [10, "hello"]; // error
Enum
상수를 표현하기 위한 Enum타입도 지원하고 있다.
enum Fruit {Apple, Orange, Cherry}
let f: Fruit = Fruit.Apple;
Scope
var vs. let
지금까지 예제들을 주의깊게 봐왔다면 변수를 선언할때 let이라는 키워드를 사용하는 것을 볼 수 있을 것이다. 기본적인 기능은 JavaScript의 var과 같은 역할을 수행하지만 let은 더 강력한 기능을 제공한다.
첫 번째로 scope의 차이가 있다. var은 function scope를 갖지만 let은 block scope을 갖는다. 그렇기에 var은 선언된 함수 전역에 영향을 미치지만 let은 영향일 미치는 범위를 줄일 수 있다.
var 아래의 코드에 문제가 없다.
function f(isChecked: boolean): number {
if (isChecked) {
var x = 10;
}
return x;
}
위 코드를 보면 var을 사용해서 변수를 선언하고 초기화하고 있다. if 블록 안에서 var키워드를 사용해서 정의하고 있으며, var은 function scope이기 때문에 에러없이 수행된다. 하지만 isChecked의 값이 false이라면 var로 변수를 선언하는 부분은 건너 뛰게 된다. 하지만 JavaScript 특성상 역시 에러가 발생하지 않으며 f 함수는 undefined를 반환할 것이다. 더 큰 문제는 f 함수보다 상위에 있는 함수 영역이나 global 변수로 x가 선언되어 있다면 그 x의 값이 반환되기 때문에 f 함수는 자신의 영역내에서 이루어지는 연산과 상관없이 이상한 값을 반환하게 될 것이다. TypeScript의 let은 블록단위로 수행된다. 그래서 let을 사용해서 위와 같은 잘못된 예제를 작성한다면 미리 에러를 확인하고 고칠 수 있다. 또한 경우에 따라서 서로 다른 영역의 원치않았던 변수가 사용되는 현상과 같이 버그를 일으킬만한 작업도 사전에 막을 수 있다.
function f(isChecked: boolean): number {
if (isChecked) {
let x = 10;
}
return x; // error, x가 정의되지 않았다!!
}
위와 같은 TypeScript 코드는 JavaScript로 변환되기 전에 에러를 발생시킬 것이다.
두 번째로 let은 중복해서 선언할 수 없다. var은 같은 같은 함수내에서 같은 이름으로 몇 번이고 다시 선언할 수 있지만, let을 그럴 수 없다.
// JavaScript, OK
function f() {
var x;
var x;
if (true) {
var x;
}
}
// TypeScript, ERROR
function fWithLet() {
let x;
let x; // error, 중복선언 불가
}
네 번쨰로 let은 블록 단위로 shadowing된다. let은 역시 블록단위로 영역이 구분되기 때문에 어찌보면 당연한 이야기다. 세 번째로 let은 capturing된다. 각 블록은 실행될때마다 선언된 변수들을 기록하는 environment가 생성된다. 그래서 내부 블록에서 외부변수를 사용하면 그 당시의 값이 캡쳐되서 environment에 저장된다. 즉, 어떤 함수가 선언됬다면 그 함수는 함수가 선언됬을때의 환경을 기억한다는 것이다. 아래의 예제에서 그 차이를 알 수 있다.
function noCapturing() {
for (var i = 0 ; i < 5 ; i++) {
setTimeout(function() { console.log(i); }, 1000);
}
}
위는 캡쳐되지 않는 var을 사용한 예제이다. 0부터 5까지 값이 증가되는 i를 출력히지만 바로 출력하지는 않고 1초 후에 출력한다. 결과값이 어떻게 나올까?
5
5
5
5
5
결과는 위와 같이 출력된다. 뭔가 의도한바와는 다른게 나오는 것 같다. 그럼 let을 사용한다면 어떨까?
function capturing() {
for (let i = 0 ; i < 5 ; i++) {
setTimeout(function() { console.log(i); }, 1000);
}
}
함수가 정의될때 해당 for 블록이 capturing되기 때문에 var을 사용할떄와는 다른 결과가 출력된다.
0
1
2
3
4
각 함수가 정의될 당시의 i값이 출력되는 것을 확인할 수 있다.
이번에는 여기까지하고, 다음 장에서는 class와 interface, function, method에 대해서 알아볼 것이다.