오늘은 타입스크립트의 제어문, 연산자, 함수에 대해서 공부한 내용을 정리해 보려고 한다. 참고로 자바스크립트에 나오는 문법과 겹치는 부분은 생략을 하고 타입스크립트에서 새롭게 추가되거나 변경된 문법들에 대해서만 다루려고 한다.
제어문
타입스크립트에서는 자바스크립트에서 사용하는 if문 및 switch문을 사용할 수 있다. if문에서 조금 헷갈릴 수 있는 부분 하나만 짚고 넘어간다. 타입스크립트에서 숫자 타입인데 숫자가 0이면 false를 나타내고, 0이 아닌 나머지 값은 true를 나타낸다. 문자열의 경우 빈 값은 false이고 값이 있으면 true이다. 아래의 예제를 참고하자.
let text: string = "";
let statusActive: number = 0;
let isEnabled: boolean = true;
// 첫 번째 if 문
if (statusActive || text) {
console.log(1); // 출력되지 않음
}
// 두 번째 if 문
if (isEnabled && 2 > 1) {
console.log(2); // 출력됨
}
switch문의 경우 자바스크립트와 사용 방법이 동일하다. 다만 switch문의 조건이 가진 타입에 맞게 case 절의 타입을 맞추어 주어야 하고, switch문의 조건 타입이 any이면 case 절에서 어떠한 타입도 가능하다. 또한 case 절에서 break 문을 사용하면 switch문을 벗어날 수 있는데, break 문을 생략하면 다음 case 절이 실행된다. 이러한 상태를 fall-through 라고 한다. 타입스크립트에서 fall-through의 사용 여부를 결정하고 싶다면 tsc 컴파일러 옵션인 'noFallThroughCaseInSwitch'를 수정하여야 한다. 이 옵션은 기본값이 false이며 이 때는 fall-through를 허용한다.
그 다음은 반복문에 대해서 알아본다. 기본적인 for 문에 대한 설명은 생략하고, ES5의 for in 문, ES6의 for of 문만 간단하게 살펴보도록 한다. ES6의 for of 문의 경우 이터러블(iterable)을 사용한다. 이터러블은 반복 가능한 객체인 배열, 문자열, DOM 컬렉션, Map, Set 등을 말한다. 마지막 예제의 경우 let이 아닌 const를 사용했다는 점에 주목하자. 일반적인 for 문이라면 const를 사용할 수 없지만, for of 문은 Symbol.iterator의 구현을 통해 각 이터레이션 값의 요소를 가져오기 때문에 const를 사용할 수 있다.
// ES5의 for in
let islands = ['Jejudo', 'Jindo', 'Ganghwado'];
for (let idx in islands) {
console.log(idx, islands[idx]);
// 0 Jejudo
// 1 Jindo
// 2 Ganghwado
}
let fruits = { 'a': 'apple', 'b': 'banana' };
for (let prop in fruits) {
console.log(prop, fruits[prop]);
// a apple
// b banana
}
// ES6의 for of
for (let value of 'hi') {
console.log(value);
// h
// i
}
for (const value of [1, 2, 3]) {
console.log(value);
// 1
// 2
// 3
}
연산자
타입스크립트는 자바스크립트와 동일한 산술 연산자 (arithmetic operator)를 지원한다. 여기에 ES7의 지수 연산자인 **를 지원하므로 Math.pow를 대체해서 사용할 수 있다. 또한 자바스크립트에서는 타입이 다른 피연산자의 더하기 연산은 예를 들어 문자열이 있을 경우 문자열 결합(concatenation)으로 인식하지만, TS에서는 타입 오류가 있다고 판단한다.
타입스크립트에서는 ES6의 디스트럭처링(destructuring)을 지원한다고 지난 포스팅에서 간단하게 언급했었다. 디스트럭처링을 매개변수로 사용하면 식이 얼마나 간단해 지는지 다음 예제를 통해서 확인해 보려고 한다. 매개변수를 디스트럭처링을 활용하여 선언하면 확실히 중복 코드를 줄일 수 있음을 알 수 있다.
// 기존 방식
function printProfile(obj) {
var name = '';
var nationality = '';
name = (obj.name == undefined) ? 'anonymous' : obj.name;
nationality = (obj.nationality == undefined) ? 'korea' : obj.nationality;
console.log(name);
console.log(nationality);
}
printProfile({ name: 'happy' });
// happy
// korea
// 매개변수 디스트럭처링 선언
function printProfile2({ name, nationality = 'none' } = { name: 'anonymous' }) {
console.log(name, nationality);
}
printProfile2(); // anonymous none
printProfile2({ name: 'happy' }); // happy none
printProfile2({ name: 'happy', nationality: 'korea' }); // happy korea
타입스크립트는 ES6의 전개 연산자(spread operator)를 지원한다. 전개 연산자는 '...'으로 나타내는데, 다음 세 가지 경우에 사용된다.
1. 나머지 매개변수를 선언할 때
2. 배열 요소를 확장할 때
3. 객체 요소를 확장할 때
함수
타입스크립트는 자바스크립트에서 함수를 선언했던 것과 동일한 방식으로 선언해 사용자가 원하는 단위 기능을 수행할 수 있도록 한다. 자바스크립트와의 다른 점이 있다면, 타입스크립트는 함수의 매개변수나 반환값에 타입을 지정해 타입 안정성(type safety)을 강화할 수 있다. 함수를 선언하는 방법은 기명 함수(named function)와 익명 함수(anonymous function)가 있다. 기명 함수는 호이스팅이 발생해서 함수를 선언하기 전에도, 선언한 후에도 호출할 수 있다. 이 점을 보완하기 위해 사용한 함수가 익명함수이다.
자바스크립트는 느슨한 타입 언어(loosely typed language)이다. 그래서 런타임시 의도하지 않은 타입 변환이 일어날 수 있는 문제가 존재한다. 그래서 타입 안정성을 갖추기 위해 타입 검사와 타입 캐스팅과 같은 불필요한 코드를 추가할 수 밖에 없다. 이러한 단점을 보완하기 위해 우리는 타입 안정성을 갖춘 타입 스크립트 함수를 사용한다.
function max(x: number, y: number): number {
if (x > y) {
return x;
} else {
return y;
}
}
let a = max(1, 2);
console.log(a); // 2
// let b = max('abc','ABC');
// 'abc' 형식의 인수는 'number' 형식의 매개 변수에 할당될 수 없습니다.
함수가 받는 매개변수의 갯수가 정해지지 않는 경우도 발생할 수 있다. 이 경우 타입스크립트에는 나머지 매개변수 기능을 사용할 수 있는데, 나머지 매개변수를 통해 0개 이상의 요소를 받을 수 있다. 나머지 매개변수의 경우 최대 개수가 정해져 있지 않기 때문에 불필요한 인수들이 함께 전달될 수 있다는 문제도 있다. 따라서 전달할 인수의 개수를 0개 이상 1개 미만으로 제한하려면 선택 매개변수를 사용해야 한다.
// 나머지 매개변수
function colors(a: string, ...rest: string[]) {
return a + ' ' + rest.join(' ');
}
let color1 = colors('red');
let color2 = colors('red', 'yellow');
let color3 = colors('red', 'yellow', 'green');
console.log(color1); // red
console.log(color2); // red yellow
console.log(color3); // red yellow green
// 선택 매개변수
function sum(a: number, b?: number) { // 선택 매개변수는 초깃값 설정을 할 수 없음
if (b === undefined) {
b = 0;
}
return a + b;
}
console.log(sum(1)); // 1
console.log(sum(1,2)); // 3
함수 오버로드(function overloads)는 함수명은 같지만, 매개변수와 반환 타입이 다른 함수를 여러 개 선언할 수 있는 특징을 말한다. 컴파일 시간에 가장 적합한 오버로드를 선택하여 컴파일 하므로 자바스크립트 실행 시에는 런타임 비용이 발생하지 않는다. 오버로드는 목록 형태로 선언할 수 있는데, 가장 일반적인 (general) 함수 (매개변수를 any 타입으로 선언)의 시그니처를 가장 아래에 선언하고 그 위로 구체적인(specific) 타입을 명시한 함수의 시그니처를 쌓는 방식으로 선언해야 한다. 그렇게 하면 선언된 함수의 시그니처에 맞게 인수를 넘겨 호출할 수 있다.
function add(a: string, b: string): string { ... }
function add(a: number, b: number): number { ... }
function add(a: any, b: any): any { ... }
화살표 함수(arrow function)는 ES6 표준에 포함된 익명 함수를 좀 더 간략하게 표현할 수 있는 방법이다. 다른 언어에서는 람다 함수(lambda function)라고 부르기도 한다. 화살표 함수는 일반적으로 변수에 할당하여 호출하기도 하지만 만약 변수에 할당하지 않고 화살표 함수를 사용하려면 즉시 호출 함수(IIF: Immediately Invoked Function)를 이용해야 한다. 화살표 함수는 필터 메서드나 리듀스 메서드에서 사용할 수도 있다.
// 화살표 함수를 필터 메서드에 적용
let numberList = [1, 2, 3, 4, 5];
numberList = numberList.filter(n => {
return n % 2 === 0;
});
console.log(numberList); // [2, 4]
// 화살표 함수를 리듀스 메서드에 적용
function getSum(nums: number[]): number {
let sum: number = nums.reduce((a, b) => { return a + b; });
return sum;
}
let numSum = getSum([1, 2, 3, 4, 5]);
console.log(numSum); // 15
익명 함수는 반환 타입을 선언할 수 있다. 하지만 익명 함수는 구현체이므로 타입을 선언하게 될 경우 형태가 다소 복잡해질 수 있다. 이러한 점을 개선하기 위해 익명 함수에 선언된 타입을 별도로 분리해 함수 타입으로 선언하면, 타입 안정성을 보장하면서도 익명 함수의 타입이 무엇인지 쉽게 파악이 가능하다. 익명 함수의 타입은 함수 타입(function type)이다.
// 함수 타입을 type 앨리어스를 통해 별도로 분리해 선언
type calcType = (a: number, b: number) => number;
let addCalc: calcType = (a, b) => a + b;
let minusCalc: calcType = (a, b) => a - b;
함수 타입을 선언하면 좋은 점은 다음과 같이 3가지 정도가 있다.
1. 익명 함수의 매개변수나 반환값에 타입을 별도로 분리할 수 있다.
2. 익명 함수에 타입을 추가하지 않아도 함수 타입만으로 익명 함수의 타입 안정성이 보장된다.
3. 익명 함수의 타입이 무엇인지는 함수 타입을 통해 곧바로 확인할 수 있으므로 가독성이 좋아진다.
콜백 함수(callback function)는 또 다른 함수의 매개변수로 전달될 수 있는 함수이다. 콜백 함수의 예로는 setTimeout 함수가 있다. 콜백 함수가 복잡해 지게 될 경우 콜백 함수의 선언을 분리해 타입을 추가하는 것이 좋다. 다음 예제를 살펴보도록 하자.
// 공통으로 사용할 콜백 함수 타입의 정의
type EchoCallbackType = (message: string) => void;
// 공통으로 사용할 콜백 함수 정의
let callbackEcho: EchoCallbackType = message => message;
let callbackEchoWithLength: EchoCallbackType = message => `${message}(${message.length})`;
function echoFunction2(message: string, callback) {
return callback(message);
}
let responseEcho = echoFunction2('hello', callbackEcho);
let responseEchoWithLength = echoFunction2('hello', callbackEchoWithLength);
console.log(responseEcho); // hello
console.log(responseEchoWithLength); // hello(5)
참고문헌
1. <퀵스타트 타입스크립트> (정진욱 저, 2018)
'Prog. Langs & Tools > TypeScript' 카테고리의 다른 글
TypeScript #6 모듈(Modules) (0) | 2020.04.06 |
---|---|
TypeScript #5 클래스와 인터페이스 Part2 (0) | 2020.03.27 |
TypeScript #4 클래스와 인터페이스 Part1 (0) | 2020.03.22 |
TypeScript #2 변수 선언과 기본 타입(feat. ES6) (0) | 2020.02.19 |
TypeScript #1 타입스크립트란 무엇인가? (0) | 2020.02.14 |