프로그램이 유용하려면 숫자, 문자열, 구조체, 불리언 값과 같은 간단한 데이터 단위가 필요하다.
TS는 JS와 거의 동일한 데이터 타입을 지원하며, 열거 탕비을 사용하여 더 편리하게 사용할 수 있다.
이번 글에서는 JS에서 볼 수 있었던 타입과 TS에서 새롭게 보게 된 타입을 구분해서 설명하려 한다.
객체는 자주 사용되기도 하고 인터페이스 같은 개념을 이해하는데에 용이하기 때문에 따로 분리해서 상단에 기술하려 한다.
JS의 객체 리터럴처럼 중괄호({}
)를 이용해 객체 타입을 표현할 수 있다.
const user: { name: string; height: number; } = {
name: '안희종',
height: 176
};
이 때 객체 타입 정의는 객체 리터럴과 아래와 같은 차이점이 있다.
:
)의 우변에는 값 대신 해당 속성의 타입이 들어간다.,
)뿐만 아니라 세미콜론(;
)을 사용할 수 있다.함수의 선택 매개변수와 비슷하게 속성명 뒤에 물음표(?
)를 붙여 해당 속성이 존재하지 않을 수도 있음을 표현할 수 있다.
const userWithUnknownHeight: { name: string; height?: number; } = {
name: '김수한무'
};
속성명 앞에 readonly
키워드를 붙여 해당 속성의 재할당을 막을 수 있다.(불변성을 지킬 수 있다.)
readonly
키워드가 붙은 속성은 const
키워드를 이용한 변수의 정의와 비슷하게 동작한다.
const user: {
readonly name: string;
height: numer;
} = { name: '안희종', height: 176 };
// error TS2540: Cannot assign to 'name' because it is a constant or a read-only property.
user.name = '종희안';
let isDone: boolean = false;
JS는 모든 숫자는 부동소수값이며 TS도 이를 지원한다.
TS는 16진수, 10진수 리터럴 포함 ES2015에 소개된 2진수, 8진수 리터럴도 지원한다.
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
문자열은 JS와 같이 큰 따옴표, 작은 따옴표, 템플릿 리터럴 모두 포함한다.
let color: string = 'blue';
let favoriteColor: string = `Hello, my favorite color is ${color}`;
TS에서 배열 타입을 두 가지 방법으로 쓸 수 있다.
// 배열 요소들을 나타내는 타입 뒤에 `[]`를 붙이는 방법
let basicList: number[] = [1, 2, 3];
// 제네릭 배열 타입
let genericList: Array<number> = [1, 2, 3];
object
는 원시 타입이 아닌 타입을 나타낸다.
(number
, string
, boolean
, bigint
, symbol
, null
, undefined
가 아닌 나머지)
object
타입을 쓰면 Object.create
같은 API를 더 잘 표현할 수 있다.
declare function create(o: object | null): void;
create({ prop: 0 }); // 성공
create(null); // 성공
create(42); // 오류
create("string"); // 오류
create(false); // 오류
create(undefined); // 공식문서에서는 오류라고 하는데 실제 ts는 오류를 발생하지 않는다.
let u: undefined = undefined;
let n: null = null;
아래부터는 TS에서 처음 본 타입들을 정리했다.
튜플 타입을 사용하면, 요소의 타입과 개수가 고정된 배열을 표현할 수 있다.
let x: [string, number];
x = ['hello', 10];
// 'string' 형식은 'number' 형식에 할당할 수 없습니다.ts(2322)
x = [10, 'hello'];
console.log(x[0].substring(1));
// 'number' 형식에 'substring' 속성이 없습니다.ts(2339)
console.log(x[1].substring(1));
// '"world"' 형식은 'undefined' 형식에 할당할 수 없습니다.ts(2322)
// 길이가 '2'인 튜플 형식 '[string, number]'의 인덱스 '3'에 요소가 없습니다.ts(2493)
x[3] = 'world';
// 개체가 'undefined'인 것 같습니다.ts(2532)
// 길이가 '2'인 튜플 형식 '[string, number]'의 인덱스 '5'에 요소가 없습니다.ts(2493)
console.log(x[5].toString());
JS의 집합과 사용하면 좋은 데이터 타입은 enum
이라고 소개한다.
C#
같은 언어처럼 enum
은 값의 집합에 의미있는 이름을 붙일 수 있다.
기본적으로 enum
은 0
부터 시작해서 멤버들의 번호를 매긴다.
멤버 중 하나의 값을 수동으로 설정하여 번호를 바꿀 수도 있다.
enum
의 유용한 기능 중 하나는 매겨진 값을 사용해 enum
멤버의 이름을 알아낼 수 있다는 것이다.
예시는 아래와 같다.
enum Color {Red = 4, Green = 7, Blue}
let c: Color = Color.Green;
let b: string = Color[8];
console.log(c, b); // 7, Blue
앱을 만들 때 알지 못하는 타입을 표현해야 할 수도 있다.
(ex: 사용자로부터 받은 데이터, 서드파티 라이브러리 같은 동적인 컨텐츠)
이 경우 타입 검사를 하지 않고, 그 값들이 컴파일 시간에 검사를 통과하길 원한다.
let notsure: any = 4;
notSure = "string";
notSure = false;
any
타입은 기존 JS로 작업할 수 있는 방법으로서 컴파일 중에 타입 검사를 하거나 하지 않을 수 있다.
다른 언어처럼 Object
가 비슷한 역할을 할 수 있을 것 같다고 생각할 수도 있는데 Object
로 선언된 변수들은 어떤 값이든 변수에 할당할 수 있게 해주지만 실제로 메서가 존재하더라도, 임의로 호출할 수 없다.
let notSure: any = 4;
notSure.ifItExists(); // 성공, ifItExists 는 런타임엔 존재할 것입니다.
notSure.toFixed(); // 성공, toFixed는 존재합니다. (하지만 컴파일러는 검사하지 않음)
let prettySure: Object = 4;
prettySure.toFixed(); // 오류: 프로퍼티 'toFixed'는 'Object'에 존재하지 않습니다.
또한 any
타입은 타입의 일부만 알고 전체는 알지 못할 때 사용할 수 있다.
let list: any[] = [1, true, "string"]
void
는 어떤 타입도 존재할 수 없음을 나타내기 때문에 any
의 반대 타입이라고도 할 수 있다.
void
는 보통 함수에서 반환 값이 없을 때 반환 타입을 표현하기 위해 사용된다.
function warnUser(): void {
console.log("This is my warning message");
}
void
를 타입 변수로 선언하는 것은 유용하지 않다.
왜냐하면 그 변수에는 null
또는 undefined
만 할당 할 수 있기 때문이다.
let unusable: void = undefined;
unusable = null; // 성공 `--strictNullChecks` 을 사용하지 않을때만
never
타입은 절대 발생할 수 없는 타입을 표현한다.
주로 함수에서 오류를 발생시키거나 절대 반환하지 않는 반환 타입으로 사용된다.
변수 또는 타입 가드에 의해 어떤 타입도 얻지 못하도록 추론되면 never
타입을 얻게 될 수 있다.
never
타입은 모든 타입에 할당 가능한 최하위 타입이다.
(심지어 any
도 never
에 할당할 수 없다.)
// never를 반환하는 함수는 함수의 마지막에 도달할 수 없다.
function error(message: string): never {
throw new Error(message);
}
// 반환 타입이 never로 추론된다.
function fail() {
return error("Something failed");
}
// never를 반환하는 함수는 함수의 마지막에 도달할 수 없다.
function infiniteLoop(): never {
while (true) {
}
}