타입스크립트는 자바스크립트 언어 상에서 설계된 강타입의 프로그래밍 언어로, 타입 안정성을 제공함으로써 런타임 에러를 줄이고 결국 생산성을 높인다.
타입스크립트는 실행 전 검사를 통한 타입 안정성을 확보하여 정적 타입 시스템의 엄격함과, 변수에 일일이 타입을 지정해주지 않아도 되는 동적 타입 시스템의 유연함을 동시에 갖추었다.
let a = "hello"
let b : boolean = "hi" // 명시적 선언
자바스크립트에서 값은 타입을 가지지만, 변수는 별도의 타입을 가지지 않는다. 따라서 자바스크립트의 변수에는 어떤 타입의 값이라도 자유롭게 할당할 수 있다. 타입스크립트는 이 변수에 타입을 지정할 수 있는 타입 시스템 체계를 구축했다. 자바스크립트의 7가지 원시 값은 타입스크립트에서 원시 타입으로 존재한다.
타입스크립트의 타입은 아래와 같은 계층구조를 가진다. 타입스크립트의 각 타입이 가지는 의미를 자세히 살펴본다. '우아한 타입스크립트', '얄팍한 코딩사전: 타입스크립트', '한 입 크기로 잘라먹는 타입스크립트: 이정환' 를 참고했다.
타입명 | 설명 | 대응하는 자바스크립트 원시 래퍼 객체 |
---|---|---|
boolean | true, false 값만 할당할 수 있다. 형 변환을 통해 Truthy 또는 Falsy로 평가되는 값은 이 타입에 해당되지 않음에 주의한다. | Boolean |
undefined | undefined 값만 할당할 수 있으며, 초기화되지 않았거나 존재하지 않음을 의미한다. optional로 지정된 타입 내 속성에도 할당될 수 있다. (아직 여기 뭘 넣을지 모르겠어) | Undefined |
null | null 값만 할당할 수 있으며, undefined와는 엄연히 다른 원시 값이기 때문에 서로의 타입에 할당할 수 없다. (비워둘 상자) | Null |
number | 숫자에 해당하는 모든 원시 값을 할당할 수 있다. | Number |
bigint | Number.MAX_SAFE_INTEGER(2^53-1)를 넘어가는 값을 처리할 수 있다. | BigInt |
string | 문자열을 할당할 수 있으며, 템플릿 리터럴(백틱(`)으로 감싼 문자열)도 할당할 수 있다. | String |
symbol | 유일무이한 성질을 갖는 값을 생성할 때 사용한다. | Symbol |
strictNullChecks
tsconfig 파일의 compilerOptions의 strictNullChecks 옵션을 true로 설정하면, null이나 undefined가 아닌 변수에 null이나 undefined를 할당하면 에러를 발생시킨다.
7가지 원시 타입에 속하지 않는 값은 모두 객체 타입으로 분류할 수 있다.
타입명 | 설명 |
---|---|
object | 객체, 배열, 정규 표현식, 함수, 클래스 등 모두 호환된다. |
{} | 객체 리터럴 방식으로 타입을 지정하며, 타이핑되는 객체가 중괄호 내의 선언 구조와 일치해야 한다. |
array | 배열 내 모든 원소들은 동일한 하나의 타입 값만 가질 수 있다. |
type & interface | {}로 타입을 매번 일일이 지정하는 것은 반복적인 작업을 수반하므로, 보통 객체를 타이핑할 때는 이 키워드들을 사용한다. |
함수(타입명 없음) | 객체의 타이핑과 달리, typeof 연산자로 확인한 function 이라는 키워드 자체를 타입으로 사용하지 않으며, 매개변수를 별도의 타입으로 지정해야 한다. |
object
특정 변수가 객체라는 것만 알 수 있어 속성 이름이나 타입 정보를 알 수 없다. 따라서 특정 속성 접근 시 타입 안정성이 보장되지 않는다. 결국 원시 타입을 제외한 모든 객체 타입을 유동적으로 할당할 수 있기 때문에 정적 타이핑의 의미가 퇴색되므로, 사용하지 않을 것을 권장한다.
const player : {
name: string,
age: number
} = {
name: "wonil"
age: 25
}
const player : {
name: string,
age?: number
} = {
name: "wonil"
}
type Player = {
name: string,
age?: number,
}
const wonil : Player = {
name: "wonil"
}
const nico : Player = {
name: "nick"
}
function add(a:number, b:number) {
return a + b
}
const add = (a:number, b:number) => a + b // Arrow function
// 매번 매개변수의 타입을 지정해주는 대신 함수자체의 타입을 미리 지정하여 재활용함
type Add = (a:number, b:number) => number;
// long version
type Add = {
(a:number, b:number) : number
}
const add:Add = (a, b) => a + b
Array<자료형>
/ 자료형[]
: 배열에 특정 자료형의 원소만을 가지게 강제한다. const array: Array<number> = [1, 2, 3];
const array: number[] = [1, 2, 3];
다차원 배열의 타입
다차원 배열도
자료형[][]
(2차원 배열),자료형[][][]
(3차원 배열)과 같이 타입을 지정할 수 있다.
읽기 전용 배열
읽기 전용 배열이란 사용할 수는 있지만 수정하지는 못하는 배열이다. readonly 키워드나 제너릭으로 읽기 전용으로 배열을 생성한다.
type Player = { readonly name: string, age?: number, }; const numbers: readonly number[] = [1, 2, 3, 4]; // 또는 const numbers: readonly ReadonlyArray<number> = [1, 2, 3, 4]; numbers.push(5); // Error
const player: [string ,number, boolean] = ["wonil", 1, true]
player[0] = 1 // Error
any
타입스크립트의 타입 검사를 비활성화하여 보호장치를 완전히 없애므로, 자바스크립트에 존재하는 모든 값을 오류 없이 받을 수 있다.
const a : any[] = [1, 2, 3, 4]
개발 과정에서 변경될 가능성이 있거나 아직 세부 항목에 대한 타입이 확정되지 않은 경우
어떤 값을 받아올지 또는 넘겨줄지 정할 수 없을 때
값을 예측할 수 없을 때 암묵적으로 사용
이런 예외적인 상황이 있음에도 불구하고, any 타입은 사실상 런타임에 타입을 검사하므로 타입스크립트를 사용하는 의미가 퇴색된다. 따라서 사용을 지양하는 것이 좋다.
unknown
무엇이 할당될지 아직 모르는 상태의 타입으로, 이 타입으로 할당된 변수는 어떤 값이든 올 수 있음을 의미하는 동시에 개발자에게 엄격한 타입 검사를 강제하는 의도를 담고 있다. 즉, any 타입을 사용함으로써 문제가 되는 상황을 보완하기 위해 등장했다고 볼 수 있다. 타입 검사를 강제하고 타입이 식별된 후에 사용할 수 있기 때문에 any 타입보다 안전하다고 볼 수 있다. 따라서 데이터 구조 파악이 힘들 때 any 대신 unknown 사용을 권장한다.
let a: unknown;
let b = a + 1 // Error
if (typeof a === 'number') {
let b = a + 1
}
let b = a.toUpperCase() // Error
if (typeof a === "string") {
let b = a.toUpperCase()
}
하지만 unknown은 any 타입을 제외한 다른 타입으로 선언한 변수에 할당할 수 없다. 가령, 아래와 같은 경우 fileItem
이 명시적으로 객체로 선언되지는 않았지만, in
키워드로 인해 객체로 추론된다. 따라서 fileItem
의 타입으로 unknown
을 쓰면 Type 'unknown' is not assignable to type 'object'.
와 같은 에러를 볼 것이다.
// TYPE GUARD: 게시물 수정 시 기존 파일 여부 체크
const isExistingFileDto = (fileItem: any): fileItem is IExistingFileDto => {
return ORIGINAL_FILE_FLAG in fileItem;
};
any | unknown |
---|---|
어떤 타입이든 any 타입에 할당 가능 | 어떤 타입이든 unknown 타입에 할당 가능 |
any 타입은 never 타입을 제외한 어떤 타입으로도 할당 가능 | unknown 타입은 any 타입 외에 다른 타입으로 할당 불가능 |
void
만나도 줄 선물이 없는 산타
함수가 어떤 값을 반환하지 않는 경우에 사용되며, 변수의 경우에는 undefined
나 null
값만 할당할 수 있지만 잘 사용되지 않는다. 사실, 함수의 경우에도 함수 내부에 별도 반환문이 없는 경우에는 타입스크립트 컴파일러가 알아서 함수 타입을 void로 추론한다.
function voidFunc(): void {
console.log("This is a function in chapter7.ts");
}
function func(): void {
console.log("This is a function.");
return undefined;
}
function anotherFunc(): undefined {
console.log("This is another function.");
return;
}
function anotherFunc(): undefined {
console.log("This is another function.");
return undefined ;
}
never
절대 만날 수 없는 산타
일반적으로 함수와 관련하여 많이 사용되며, 값을 ‘반환할 수 없는’ 타입이다.
function hello(name:string|number) {
if (typeof name === "string") {
} else if (typeof name === "number") {
} else {
name // never => 도달하면 안되는 코드(name의 타입은 string이나 number 중에서 정해져야 함)
}
}
function generateError(res: Response): never {
throw new Error(res.getMessage());
}
function checkStatus(): never {
while (true) {
...
}
}
function handleValue(x:string|number|boolean|object) {
if (typeof x === 'string') {
console.log(x.toUpperCase());
} else if (typeof x === 'number') {
console.log(x.toFixed(2));
} else if (typeof x === 'boolean') {
console.log(x ? 'true' : 'false');
} else {
// 아랫줄에서 오류가 발생
// Type 'object' is not assignable to type 'never'.
const _exhaustiveCheck: never = x;
console.log(_exhaustiveCheck);
}
}
enum
열거형이라도 부르며, 일종의 구조체를 만드는 타입 시스템이다. 주로 문자열 상수를 생성하는 데 사용된다.
enum Os {
Window = 3,
Ios = 11,
Android = 'win'
}
console.log(Os['Ios']) // 11
console.log(Os[11]) // "Ios"
const enum
숫자로만 이루어졌거나 타입스크립트가 자동으로 추론한 열거형은 할당된 값을 넘어선 범위로 역방향 접근을 하더라도 에러를 발생시키지 않는다. 따라서, 이러한 동작을 막기 위해서
const enum
을 사용한다.
타입스크립트는 tsc라고 불리는 컴파일러를 통해 자바스크립트 코드로 변환된다. 하지만 타입스크립트는 고수준 언어가 저수준 언어로 변환되는 것이 아닌, 고수준 언어(타입스크립트)가 또 다른 고수준 언어(자바스크립트)로 변환되는 것이므로 컴파일이 아닌 트랜스파일(Transpile)이라 부르기도 한다. 또한 소스코드를 다른 소스코드로 변환한다는 관점에서 소스 대 소스 컴파일러(Source-to-Source Compiler)라고도 한다.
컴파일(Compile)
개발자가 작성한 소스코드를 컴퓨터가 이해할 수 있는 바이트 코드로 변환하는 과정
[tsc] 타입스크립트 소스코드를 타입스크립트 AST(Abstract Syntax Tree, 최소 구문 트리)로 만든다.
[tsc] 타입 검사기가 AST를 확인하여 타입을 검사(Type Checking)한다.
타입스크립트 소스 코드의 타입은 1~2단계에서만 사용되며, 최종적으로 만들어지는 프로그램에는 아무런 영향을 주지 않는다.
[tsc] 검사 성공 시, 타입스크립트 AST를 자바스크립트 소스로 변환한다(검사 실패 시에는 컴파일 종료됨). 이때, 타입과 관련한 코드들은 컴파일 결과 모두 사라진다.
[런타임] 자바스크립트 소스코드를 자바스크립트 AST로 만든다.
[런타임] AST가 바이트 코드로 변환된다.
[런타임] 런타임에서 바이트 코드가 평가되어 프로그램이 실행된다.
AST(Abstract Syntax Tree, 추상 문법 트리)
컴파일러가 소스 코드를 해석하는 과정에서 생성된 데이터 구조이며, 어휘적 분석과 구문 분석을 통해 소스 코드를 노드 단위의 트리 구조로 구성한다.