자바스크립트에 타입을 부여한 언어
자바스크립트의 모든 기능을 포함하면서 정적 타입을 지원한다. (자바스크립트는 동적 타입)
자바스크립트 기능들을 제공하면서, 그 위에 자체 레이어를 추가 -> 즉, 자바스크립트의 확장된 언어이다.
타입스크립트는 본래 자바스크립트가 구현할 수 없었떤 타입체킹과 같은 기능을 구현하고, 컴파일러를 통해 JS로 변환한다.
function sum(a: number, b: number) {
return a + b;
}
sum(10, 20); // 30
sum('10', '20') // 1020
// Error: '10'은 number에 할당될 수 없습니다.
자동완성 및 추천, 디버깅 등 유용한 기능들을 제공하기 때문에 생산성이 증가한다.
function sum(a: number, b: number) {
return a + b;
}
const result = sum(10, 20);
result.toLocaleString(); // 특정 언어의 표현 방식에 맞게 숫자를 표기하는 메서드
result
라는 변수의 타입이 코드를 작성하는 시점에 number
라는 것을 JS는 인지하지 못하나, TS를 사용할 경우 result
에 대한 타입이 지정되어 있기 때문에 편집기에서 해당 타입에 대한 메서드를 미리보기로 띄워줄 수 있다.
Primitive Types (원시 타입)
- boolean: 참 또는 거짓을 나타내는 논리 데이터 타입
- number: 모든 숫자를 나타내는 데이터 타입
- string: 문자열을 나타내는 데이터 타입
- null: null 자체인 특수한 데이터 타입 (명시적 없음)
- undefined: undefined 자체인 특수한 데이터 타입 (암시적 없음)
- symbol: ES6에서 도입된, 변경 불가능하고 고유한 값인 심볼을 나타내는 데이터 타입
- bigint: 큰 정수를 표현할 수 있는 새로운 데이터 타입 (Es2020에 도입)
Basic Types
- object: 객체를 나타내는 데이터 타입
- array: 배열을 나타내는 데이터 타입 (variable-length)
- tuple: 배열의 길이가 고정되고 각 요소의 타입을 지정할 수 있는 배열 형식(fixed-length)
- enum: 특정 값(상수)들의 집합
- any: 모든 타입에 대해서 허용
- void: 반환 값이 없는 함수의 반환 타입
- never: 함수의 끝에 절대 도달하지 않는다는 의미를 지닌 타입
// 타입 표기 예시
let str: string = 'hi'; // string
let num: number = 10; // number
let isLoggedIn: boolean = false; // boolean
let arr: number[] = [1, 2, 3]; // array
let arr: Array<number> = [1, 2, 3]; // array-generic
let arr: [string, number] = ['hi', 10]; // tuple
let arr: any = ['a', 2, true]; // any
// 변수를 선언할 때 꼭! 항상! 타입을 써야하는 건 아니나,
// 자동으로 추론된 타입에 대해서도 fixed type이 됨
function printSomething(): void {
console.log('sth');
} // void
function neverEnd(): never {
while (true) {
}
} // never
function greet(name: string) {
console.log("Hello, " + name.toUpperCase() + "!!");
} // 매개변수 타입 표기
any는 모든 종류의 값에 사용될 수 있지만, 해당 변수에 대한 타입 검사를 완전히 무시하게 되므로 실수를 컴파일단계가 아닌 런타임에서만 발견할 수 있게 되어 TS의 의미가 퇴색된다.
-> 따라서 가능한 any 대신 명확한 타입을 지정해야 하며, 변수가 어떤 타입을 가질지 예측할 수 없거나 알 필요가 없는 경우에나 제한적으로 사용해야 한다.
Union Type
|
연산자를 사용하여 타입을 여러 개 연결하는 방식
function printId(id: number | string) {
console.log("Your ID is: " + id);
}
printId(101); // OK
printId("202"); // OK
print({ myID: 22342 }); // Error
상호 간에 정의한 약속 혹은 규칙을 말한다.
타입스크립트에서는 객체 타입을 단순 Object로 선언할 수 없으므로, interface를 통해 key, value까지 타입을 선언한다.
// 인터페이스명은 PascalCase 사용
interface Human {
name: string; // name 키는 문자열 타입
age: number; // age 키는 number 타입
boo(): void; // boo 함수는 void 타입
}
// 인터페이스 자체를 타입으로 주어서 객체 생성
const person: Human = {
name: "da",
age: 5,
boo: () => console.log("this is boo!"),
};
// 매개변수에서 인터페이스를 타입으로 받기
function booboo(a: Human): void {
console.log(`${a.name} is ${a.age} years old!`}
};
booboo(person); // da is 5 years old
person.boo(); // this is boo!
객체 타입은 일부 또는 모든 프로퍼티의 타입을 선택적인 타입, 즉 optional로 지정이 가능 -> 프로퍼티 이름 뒤에 ?
표기
interface CraftBeer {
name: string;
hop?: number;
}
let myBeer = { // :CraftBeer 가 빠졌나?
name: 'Saporo'
};
function brewBeer(beer: CraftBeer) {
console.log(beer.name); // Saporo
}
brewBeer(myBeer);
읽기전용속성(Readonly Property)는 인터페이스로 처음 생성할 때만 값을 할당하고, 그 이후에는 변경할 수 없다.
인터페이스로 객체를 처음 선언하여 값을 대입한 이후, 따로 프로퍼티에 접근해서 수정하려고 하면 오류가 발생한다.
interface User {
name: string;
age: number;
gender?: string;
readonly birthYear: number; // 읽기 전용 속성
}
let user: User = {
name: 'jeff',
age: 30,
birthYear: 2010, // 최초에 값을 초기화 할 때만 할당 가능
};
user.birthYear = 1999; // Error
특정 타입이나 인터페이스를 참조할 수 있는 타입 변수를 의미한다.
정의한 타입에 대해 나중에 쉽게 참고할 수 있게 이름을 부여하는 것과 동일하다.
// string 타입 사용
const name: string = 'capt';
// 타입 별칭 사용
type MyName = string;
const name: MyName = 'capt';
type Test = {
a: number;
b: number;
}
TypeAlias vs Interface
둘의 가장 큰 차이는 타입의 확장 가능 / 불가능 여부
-> Interface는 확장이 가능한데 반해, TypeAlias는 확장 불가능
TypeAlias 방식에서는 intersection(&), union(|) 키워드와 tuple 사용 가능
타입스크립트 공식문서에서는 interface를 사용하여 타입을 선언할 것을 권장한다.
interface Point{
x: number;
y: number;
z: number;
};
type PartialPointX = { x: number };
type PartialPointY = { y: number };
// intersection
type IntersectionPoint = PartialPointX & PartialPointY;
// union
type UnionPoint = PartialPointX | PartialPointY;
// tuple
type Data = [number, string];
타입스크립트에서 제네릭(Generic)을 사용하면, 컴포넌트나 함수 등에서재사용 가능한 코드를 작성할 수 있다. -> 여러 종류의 타입에 대응, 타입 안정성이 보장된다.
매개변수의 괄호 바로 옆에 화살괄호<>
로 묶어서 별칭을 배치해 제네릭으로 만들 수 있다.
// 상태 관리를 위해 useState 훅을 사용할 때 제네릭을 활용
import React, { useState } from 'react';
function MyComponent() {
const [state, setState] = useState<Array<string>>([]);
// ...
}
위 코드에서useState<Array<string>>([])
부분은 상태 값이 문자열의 배열이 될 것임을 명시적으로 지정 -> 이 상태와 관련된 모든 작업에 대해 타입 체크 수행이 가능함
// value와 setValue라는 두 가지 속성을 가진 제네릭 인터페이스
interface State<T> {
value: T;
setValue: (newValue: T) => void;
}
function useGenericState<T>(initialValue: T): State<T> {
const [value, setValue] = useState<T>(initialValue);
return { value, setValue };
}
// 예시
const stringState = useGenericState<string>('hello');
stringState.setValue('new String'); // OK
const numberState = useGenericState<number>(123);
numberState.setValue(456); // OK
제네릭(Generic)을 사용하여 상태(state)를 관리하는 useGenericState
함수와 그를 사용하는 예시코드이다.
useGenericState
함수는 제네릭 T
를 인자로 받아서 초기값과 함께 상태값을 설정하고 반환한다.
어떤 타입의 상태라도 동일한 로직으로 관리할 수 있으며, 각각의 경우에 대해 타입 안정성을 보장한다.
useGenericState
함수를 사용하여stringState
와numberState
상태를 생성하고,setValue
를 통해 값을 변경한다.
stringState
는 문자열 타입의 상태로 초기값을 'hello'로 설정하고,stringState.setValue
를 사용하여 값을 'new String'으로 변경하는 것이 가능하다.
마찬가지로numberState
는 숫자 타입의 상태로 초기값을 123으로 설정하고,numberState.setValue
를 사용하여 값을 456으로 변경하는 것이 가능하다.