TypeScript
는 기존 JavaScript
Type과 더불어 다음과 같은 추가 Type을 사용할 수 있다.
String
: 문자열
Number
: 숫자
Boolean
:true
/false
Array
: 배열
Object
: 객체
Null
: 빈 값
Undefined
: 정의되지 않은 값
symbol
: 수정 불가능한 고유값
Tuple
: 길이와 각 요소의 타입이 정해진 배열
Enum
: 특정 값들의 집합
Any
: 모든 타입
unknown
: 확실하지 않은 타입
Void
: return 값이 없는 함수 타입
Never
: 항상 오류를 발생시키거나, return 값이 없는 함수 타입
TypeScript
는 특정 변수나, 값의 Type을 지정하기 위해 콜론( : ) + 타입명
을 사용한다.
Primitive Type
(String
, Number
, Boolean
, Null
, Undefined
)은 다음과 같이 표기한다.
const str: string = 'hello world'; // 문자열
const num: number = 0; // 숫자
const bool: boolean = true; // boolean
const nl: null = null; // null
const uf: undefined = undefined; // undefined
Array
는 type[]
, 제네릭 표기법 (Array<type>)
의 방법으로 표기할 수 있으며, Union( | )
을 사용하면 Type을 추가할 수 있다.
const arr1: number[] = [1, 2, 3];
const arr2: string[] = ["1", "2", "3"];
const arr3: Array<number> = [1, 2, 3];
const arr4: Array<string> = ["1", "2", "3"];
const arr5: (number | string)[] = [1, '1', 2, '2'];
const arr6: Array<number | string> = [1, '1', 2, '2'];
Object
는 typeof
연산자가 object
로 반환하는 모든 타입을 나타내며, 객체 각각의 타입을 개별적으로 표기한다.
const obj1: object = {};
const obj2: { name: string, price: number } = {
name: 'apple',
price: 100
};
Function
은 매개변수, return값의 Type을 표기한다.
function add(num1: number, num2: number): number {
return a + b;
}
add(1, 2);
Tuple
은 길이가 가변적인 Array
와는 달리 미리 요소의 갯수를 정하여 고정적인 배열을 표현한다.
그래서 미리 정해진 요소의 갯수만큼 타입 또한 미리 선언한다.
const tuple1: [string, number] = ['1', 1];
const tuple2: [string, number] = ['1', '1']; // Error
const tuple3: [string, number] = ['1', 1];
tuple3[1].length; // Error (tuple3[1]의 Type은 number)
tuple3[2] = "2"; // Error (정의되지 않은 인덱스)
Enum
은 Enumerated Type(열거형)
을 의미하며, 값들의 집합을 명명하고 이를 사용할 수 있게 한다.
일반적으로 사용하고 있는 열거자들은 주로 식별자이며 대표적으로 Boolean
을 예로 들 수 있다.
기존 JavaScript
는 Enum
을 제공하지 않아 상수들의 집합을 정의할 수 없었지만, TypeScript
에서는 이를 해결하고자 Enum
을 지원한다.
값은 기본적으로 0부터 시작하며, 이 후 1씩 증가한다.
enum obj { a, b, c };
console.log(obj);
// 0: "a"
// 1: "b"
// 2: "c"
다음과 같이 의도적으로 인덱스를 변경할 수도 있다.
enum obj { a = 1, b = 2, c = 3 };
const a: String = obj[1];
enum fruits { Apple, Banana, Grape }
const eat: fruits = fruits.Apple;
Any
는 모든 Type을 허용한다.
주로 Type 선언이 어려운 경우에 사용되며, Compile
과정에서 Type을 검사하지 않아 에러 발생에 주의해야 한다.
let anything: any = 1;
any = 'Hello World';
console.log(anything); // Hello World
any = [1, 2, 3];
console.log(anything); // [1, 2, 3]
Void
는 변수에 undefined
또는 null
만 할당할 수 있으며, 함수에서는 return값이 없는 경우 할당할 수 있다.
const n: void = undefined;
function sayHello(): void {
console.log("Hello!!");
}
console.log(sayHello()); // undefined
Never
는 값의 공집합이며, 집합에 아무런 값이 존재하지 않기 때문에 Any
타입의 값을 포함한 어떠한 값도 가질 수 없다.
그래서 Never
타입은 때때로 점유할 수 없는, 혹은 바닥 타입이라고 불린다.
declare const any: any
const never: never = any // Any타입은 Never타입에 할당할 수 없다.
Union
은 집합의 합집합을 뜻하며, JavaScript
의 OR 연산자(||)
와 같이 'A' 또는 'B'
라는 의미의 타입으로 주로 여러 타입을 지정할 때 사용한다.
Union
Type을 사용할 때는 '|'
기호를 사용하여 타입을 열거한다.
let type = string | number; // type = string or type = number
Intersection
은 집합의 교집합을 뜻하며, JavaScript
의 AND 연산자(&&)
와 같이 'A' 와 'B'
라는 의미의 타입으로 지정한 여러 타입을 모두 만족하는 하나의 타입을 지정할 때 사용한다.
Intersection
Type을 사용할 때는 '&'
기호를 사용하여 타입을 열거한다.
type type1 = string;
type type2 = number;
type type3 = type1 & type2
Optional Parameter
는 값의 존재 유무가 확실하지 않은 경우에 사용되며, '?'
기호를 사용하여 필수 요소 유무를 제어할 수 있다.
function add(num1: number, num2?: number) {
console.log(num1 + num2);
}
add(1, 2); // 3
add(1); // 1
TypeScript
는 일반적인 타입 변환을 쉽게 하기 위해 전역적으로 사용할 수 있는 다양한 Utility
Type을 제공한다.
대표적인 종류는 다음과 같으며, 공식 홈페이지를 통해 추가 Type을 확인할 수 있다.
Partial<T>
: T의 프로퍼티를 선택적으로 구성
Readonly<T>
: T의 프로퍼티를 읽기 전용으로 설정하여, 값을 재할당하는 경우 에러가 발생
Record<T, K>
: 프로퍼티 키를 K, 값을 T로 하는 타입을 생성
Pick<T, K>
: T 타입 중에서 K 프로퍼티만 지정하여 타입을 생성
Omit<T, K>
: T 타입의 모든 프로퍼티 중 K를 제거하여 타입을 구성
Exclude<T, U>
: 타입 T에서 U와 겹치는 타입을 제외한 타입을 구성
Extract<T, U>
: 타입 T에서 U와 겹치는 타입만 포함하여 타입을 구성
NonNllable<T>
: T 타입에서null
과undefined
를 제외한 타입을 구성
Parameter<T>
: 함수 타입 T의 매개변수의 타입들의 튜플로 타입을 구성
ConstructorParameters<T>
: 클래스의 생성자를 비롯한 생성자 타입의 모든 매개변수 타입을 추출
ReturnType<T>
: 함수 T가 반환한 타입으로 타입을 구성
Required<T>
: 타입 T의 모든 프로퍼티가 필수로 설정된 타입을 구성
Class
는 객체 지향 프로그래밍(Object-oriented programming, OOP)
에 사용되는 대표적인 문법으로 프로그래밍을 객체 단위로 나눠 작성하는 것이다.
객체 지향 프로그래밍의 장점은 다음과 같다.
- 프로그램을 유연하고 변경이 용이하게 만든다.
- 프로그램의 개발과 보수를 간편하게 만든다.
- 직관적인 코드 분석을 가능하게 한다.
- 객체 지향 프로그래밍의 특성: 강한 응집도, 약한 결합도를 지향
Class
는 필드(field)
, 생성자(constructor)
, 메소드(method)
등의 요소로 구성되며, 해당 요소들을 묶어 Class
의 멤버(member)
라고 한다.
또한 이를 통해 생성된 객체는 인스턴스(instance)
라고 부른다.
클래스 문법의 대표적인 특징은 다음과 같다.
- 클래스를 생성하기 위해서는
class
키워드를 사용한다.- 클래스 내에서 클래스 멤버를 사용하기 위해서는
this
키워드를 사용한다.- 클래스 인스턴스를 생성하기 위해서는
new
키워드를 사용한다.
Class
에서 정의된 변수는 속성(Property)
이라 부르며, this
키워드를 통해 접근할 수 있다.
또한 Class
내부에서 Constructor
키워드를 사용하여 생성자(Constructor)
를 정의할 수 있다.
class Apple {
name: string; // Property
constructor() { // Constructor
this.name = 'apple'; // Constructor를 통해 Property 할당
}
}
접근 제어자는 Class
의 접근을 제어하기 위해 사용되며, 각각의 접근제어자(public
, protected
, priavte
)는 접근 범위 제한을 통해 나눠진다.
public
: 모두 접근 가능하며, default 값protected
: 해당 클래스와 하위 클래스만 접근 가능priavte
: 클래스 외부에서 접근 불가
Readonly
는 Class member
의 수정을 허용하지 않기 위해, 즉 읽기 전용으로 설정하기 위해 사용된다.
readonly
키워드를 사용하여 설정할 수 있으며 설정한 이후에는 값의 수정이 불가능하다.
class Apple {
readonly name: string;
constructor() {
this.name = 'apple'; // Error
}
}
Static
은 Class
의 특정 정적 멤버(메서드 또는 속성)를 전역적으로 공유하기 위해 사용되며, 이를 위해 해당 멤버에게 static
키워드를 사용한다.
그래서 Static
을 사용하면 인스턴스를 생성하지 않고도 Class
멤버에 접근할 수 있다.
class Apple {
static name: string = 'apple';
}
console.log(Apple.name); // apple
객체 지향 프로그래밍은 상속이라는 개념을 이용하여 기존의 클래스를 확장하고 이를 상속받는 새로운 클래스를 생성할 수 있다.
Class
의 상속은 extends
키워드를 사용하며, 상속을 받은 자식 클래스는 부모 클래스의 member
뿐만 아니라 자신만의 member
또한 추가로 정의할 수 있다.
class Parent {
name: string = 'apple';
sayName() { console.log('apple') };
}
class Child extends Parent {
// name: string = 'apple';
// sayName() { console.log('apple') };
speakName() { console.log('banana') };
}
특정 속성에 접근할 때마다 그 값을 명시적인 함수 호출 없이 보여주고자할 때 getter
를 사용하며, get
키워드를 사용하여 정의할 수 있다.
특정 속성 값이 변경되는 함수를 실행할 때 setter
를 사용하며, set
키워드를 사용하여 정의할 수 있다.
class Shape {
private name: string = 'apple';
get sayName() {
console.log(this.name);
}
set setName(text) {
this.name = text;
}
}
Abstract
는 추상 클래스를 의미하며 기존 Class
와는 다르게 인스턴스화가 불가능하다.
그래서 추상 클래스를 사용하기 위해서는 상속과정이 반드시 필요하다.
추상 클래스나 추상 메소드를 정의할 때는 abstract
키워드를 사용하며 추상 클래스를 상속받은 클래스는 추상 클래스의 member
를 구현해야 한다.
abstract class Parent {
abstract sayHello(): void;
}
class Child extends Parent {
sayHello() { console.log('Hello') }
}
Interface
는 상호 간에 정의한 약속 혹은 규칙을 의미하며, interface
키워드를 사용하여 정의한다.
즉, Interface
를 통해 값이 특정한 형태를 갖도록 정의, 제약할 수 있기 때문에 일반적으로 변수, 함수, 클래스의 타입을 체크하기 위해 사용된다.
또한, Interface
는 프로퍼티와 메소드를 가질 수 있다는 점에서 Class
와 역활이 비슷해보이지만 Interface
는 직접 인스턴스를 생성할 수 없으며 모든 메소드가 추상 메소드이다.
interface Fruit {
name: string;
color: string;
price: number;
}
const apple: Fruit = { name: 'apple', color: 'red', price: 100};
function sayFruit(f: Fruit): void {
console.log(f.name);
console.log(f.color);
console.log(f.price);
}
인터페이스에 정의되어 있는 속성 중 필수로 사용되지 않는, 즉 Optional
한 값을 표현할 때는 '?'
를 사용한다.
Optional
이 포함된 Interface
는 모든 요소를 필수로 사용하지 않아도 된다.
interface Fruit {
name: string;
color: string;
price?: number;
}
const apple: Fruit = { name: 'apple', color: 'red' };
Class
와 마찬가지로 Interface
또한 상속받을 수 있다.
단, Interface
간의 상속은 extends
키워드를 사용하지만 Class
와 Interface
간의 상속은 implements
키워드를 사용한다.
interface Parent {};
interface Child1 extends Parent {};
class Child2 implements Parent {};
추상(Abstract) 클래스는 앞서 설명했듯 상속(extends)을 통해 추상 메소드의 구현을 강제하기 때문에 선언과 구현이 모두 존재한다.
하지만 Interface
는 이와 다르게 모든 구현에 대한 틀만 정해놓기 때문에 선언만 존재한다.
그래서 보통 추상 클래스는 프로그램의 전체 구조를 위해 사용되며, Interface
는 기본적인 설계 용도로 사용된다.
Generic
은 선언 시점이 아닌 생성 시점에 타입을 명시하여, 특정 자료형에 국한되지 않고 동적으로 여러 타입을 사용한다.
즉, 한번의 선언으로 다양한 타입을 재사용하여 하나의 타입만이 아닌 여러 타입을 사용할 수 있도록 한다.
Generic
은 통상적으로 Type의 약자인 T를 통해 타입 변수를 선언하며, 이는 관용적으로 사용하는 값이기 때문에 다른 변수를 사용해도 무방하다.
function print<T>(arg: T): T { // T에는 동적으로 타입이 할당
console.log(arg);
}
print(1); // 1
print('Hello'); // Hello
print([1, 2, 3]); // [1, 2, 3]
Generic
에서는 제약조건을 통해 개발자가 원하지 않는 속성에 접근하는 것을 방지할 수 있다.
특정 타입만을 허용할 때는 extends
키워드를 사용하며, 두 객체를 비교할 때는 Keyof
키워드를 사용한다.
const print = <T extends number>(args: T): T => {
console.log(args);
}
print(1); // 1
print('Hello'); // Error
function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
obj[key] = value;
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
setProperty(person, 'name', 'Anna')
TypeScript: JavaScript With Syntax For Types.
React TypeScript Tutorial for Beginners - Codevolution
타입스크립트 입문 - 기초부터 실전까지 - 장기효