타입스크립트의 변수 선언
타입스크립트에서 변수 선언 방식은 var, const, let 이렇게 세 가지가 있다.
1. var
전역 공간이나 함수 내에서 변수를 선언할 때 var 선언자를 사용한다. var 로 선언된 변수는 다음과 같은 두 가지 특성이 있다. 첫 번째는 호이스팅(Hoisting)이다. 호이스팅은 선언한 변수가 스코프의 최상위로 끌어올림 되는 현상이다. 두 번째는 함수 레벨 스코프(function level scope)를 지원한다는 점이다. 이는 함수 내에서 선언한 변수는 함수 내에서만 유효하고 함수 외부에서는 참조할 수 없음을 의미한다.
// var는 함수 레벨 스코프를 지원
var myName = "sad";
function functionLevel() {
var myName = "happy"; // 스코프가 달라 sad에 영향을 미치지 않음
}
functionLevel();
console.log(myName); // sad
// var는 블록 레벨 스코프를 지원하지 않음
var yourName = "sad";
if(true) {
var yourName = "happy"; // sad와 같은 스코프로 인식
}
console.log(yourName); // happy
이러한 var가 가질 수 있는 문제점은 다음과 같다. 할당을 먼저 하고 나중에 선언하는 코드이기 때문에 가독성이 떨어진다. 또한 위의 예제처럼 if 문 안의 내부 변수는 if 문 밖의 외부 변수에 영향을 줄 수 있다. 동일한 변수를 실수로 여러번 선언 할 수도 있게 된다. 이러한 문제점을 해결하기 위해 블록 레벨 스코프(block level scope)를 지원하는 let과 const가 ES6 및 TS에서는 지원이 된다.
2. let
let 선언자의 특징은 다음과 같다. 같은 블록 내에서 같은 이름의 변수를 중복해서 선언할 수 없으며, 변수를 초기화 하기 전에 변수에 접근할 수 없게 해서 호이스팅을 방지한다. 그리고 블록 레벨 스코프가 적용이 된다.
// let은 블록 레벨 스코프를 지원
let myName2 = "sad";
if (true) {
let myName2; // 스코프가 달라 영향이 없음
myName2 = "happy"; // 변수 선언 뒤에만 할당 가능(호이스팅 문제 해결)
}
console.log(myName2); // sad
3. const
const 역시 ES6 및 TS의 특징이며 블록 레벨 스코프를 지원하며, 호이스팅을 일으키지 않는다. let과의 차이점은 const를 사용했을 때 초기화는 가능하지만 재할당은 가능하지 않다는 점이다.(read only) 다만 참조하는 값이 불변한다는 개념과는 혼동하면 안 된다.
const kitty = {
name: "Aurora",
numLives: 10,
}
// 아래는 모두 가능하다
kitty.name = "Rory";
kitty.name = "Cat";
kitty.numLives--;
비구조화(Destructuring)
타입 스크립트의 또 다른 ES6 특징은 비구조화이다. 비구조화에는 크게 배열 비구조화와 객체 비구조화가 있다.
배열 비구조화 (Array Destructuring)
// 비구조화 할당
let input = [1, 2];
let [first, second] = input;
console.log(first); // 1
console.log(second); // 2
// ... 구문을 사용하여 나머지 항목에 대한 변수 생성
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [2, 3, 4]
// 후행 요소는 무시가 가능하다
let [first] = [1, 2, 3, 4];
console.log(first); // 1
let [, second, , fourth] = [1, 2, 3, 4];
객체 비구조화 (Object Destructuring)
// 객체 해체
let o = {
a: "foo",
b: 12,
c: "bar",
};
let { a, b } = o;
// 선언 없이 할당 가능
({ a, b } = { a: "baz", b:101 });
// 프로퍼티 이름 변경 변경
let { a: newName1, b: newName2 } = o;
let newName1 = o.a;
let newName2 = o.b;
함수 선언 (Function Declarations) : 비구조화는 여기서도 쓰인다!
// 예시
type C = { a: string, b?: number }
function f({ a, b }: C): void {
// ...
}
// 기본값 앞에 패턴을 둔다
function f({ a, b } = { a: "", b: 0 }): void {
// ...
}
f(); // 기본값 { a: "", b: 0 }
타입 검사와 타입 선언
타입스크립트는 점진적 타입 검사(gradually type checking)를 수행한다. 점진적 타입 검사는 컴파일 시간에 타입 검사를 수행하면서 필요에 따라 타입 선언의 생략을 허용하기도 한다. 타입 선언을 생략하면 암시적(implicit) 형변환이 일어난다. 타입스크립트에서 점진적 타이핑을 설명할 때 가장 적절한 타입은 any 타입이다. any 타입은 모든 타입의 최상위 타입이다. any 타입으로 선언된 변수는 어떤 타입의 변수도 받아들이면서 심지어 타입이 없는 변수도 받아들인다.
타입스크립트의 타입 계층도는 다음과 같다. 타입스크립트에서는 모든 타입을 받을 수 있는 any 타입이 가장 상위에 있고 그 아래로 기본 타입, 객체 타입, 기타 타입(유니언 타입, 인터섹션 타입) 등이 있다.
1. 기본 타입
기본 타입(primitive)은 보편적으로 많이 사용되는 내장 타입으로서 타입스크립트에서 지원하는 기본타입의 종류는 다음과 같다.
string, number, boolean, symbol(ES6), enum, 문자열 리터럴
number 타입은 ES6에서 10진수 뿐만 아니라 16진수, 2진수, 8진수도 지원한다.
let decimal: number = 6; // 10진수
let hex: number = 0xf00d; // 16진수
let binary: number = 0b1010; // 2진수
let octal: number = 0o744; // 8진수
symbol은 Symbol() 함수를 이용해 생성한 고유하고 수정 불가능한 데이터 타입으로 객체 속성의 식별자로 사용된다. Symbol 함수는 심벌 객체를 반환한다. 이 때 Symbol 함수가 유일한 식별자를 생성하는 팩토리 함수의 역할을 한다. Symbol 함수를 호출할 때 "hello" 인수는 심벌의 설명을 의미한다.
let hello = Symbol("hello");
2. 객체 타입
객체 타입은 속성을 포함하고 있으며, 호툴 시그니처(call signiture), 생성자 시그니처(construct signiture) 등으로 구성된 타입이다. 타입스크립트에서 지원하는 객체 타입의 종류는 다음과 같다.
Array, Tuple, Function, 생성자, Class, Interface
배열의 타입은 두 가지 방법 중 하나로 작성이 될 수 있다. 하나는 요소 타입(element type)이고, 다른 하나는 제네릭 타입(generic type)이다.
let array: number[] = [1, 2, 3];
let genericArray: Array<number> = [1, 2, 3];
tuple은 배열 요소가 n개로 정해질 때 각 요소별로 타입을 지정한 타입이다. 예를 들어 배열 요소가 문자열과 숫자라면 [string, number] 같은 형태로 타입을 정의한다.
let x: [string, number];
x = ["tuple", 100];
3. 기타 타입
그 밖에 타입스크립트에서는 다음과 같은 타입을 지원한다.
유니언, 인터섹션, 특수 타입
유니언(Union) 타입은 2개 이상의 타입을 하나의 타입으로 정의한 타입이다.
인터섹션 타입은 두 타입을 합쳐 하나로 만들 수 있는 타입이다. 예를 들어 Cat 인터페이스와 Bird 인터페이스에 선언된 속성을 합치기 위해 Cat & Bird와 같은 방식으로 선언할 수 있다.
// 유니온 타입
var x = string | number;
// 인터섹션 타입
interface Cat { leg: number; }
interface Bird { wing: number; }
let birdCat: Cat & Bird = { leg: 4, wing: 2 };
any 타입은 제약이 없는 타입으로 어떤 타입의 값도 받아들일 수 있다. 최소한의 타입 검사만 수행하며, 임의의 값을 받아들일 때에도 any 키워드로 선언할 수 있다. any 타입은 확정된 타입은 아니지만 어떤 값이든 할당받을 수 있다. any 타입에서는 선언되지 않은 어떠한 메서드를 호출해도 컴파일 에러가 발생하지 않는다. 비슷한 타입으로 object 타입이 있다. object 타입은 any 타입처럼 타입 구분 없이 값을 할당할 수 있다. 차이점은 any 타입으로 선언한 변수는 속성의 유무를 런타임 시에 검사하지만, object 타입으로 선언한 변수는 컴파일 시간에 속성의 유무를 검사한다는 점이다.
참고자료
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 #3 제어문, 연산자 그리고 함수 (0) | 2020.02.27 |
TypeScript #1 타입스크립트란 무엇인가? (0) | 2020.02.14 |