TypeScript
docs
- 자료형 및 강력한 객체지향 아키텍처를 지원
tsconfig.json
compile config 내용을 작성하는 json 파일
Type Declaration
변수 타입선언
let foo: string = 'hello';
let bar: number = true; // error
매개변수 타입선언
function multiplyA(x: number, y: number): number {
return x * y;
}
const multiplyB = (x: number, y: number): number => x * y;
Type
Type | JS | TS | Description |
---|---|---|---|
boolean | ◯ | ◯ | true와 false |
null | ◯ | ◯ | 값이 없다는 것을 명시 |
undefined | ◯ | ◯ | 값을 할당하지 않은 변수의 초기값 |
number | ◯ | ◯ | 숫자(정수와 실수, Infinity, NaN) |
string | ◯ | ◯ | 문자열 |
symbol | ◯ | ◯ | 고유하고 수정 불가능한 데이터 타입이며 주로 객체 프로퍼티들의 식별자로 사용 (ES6에서 추가) |
object | ◯ | ◯ | 객체형(참조형) |
array | ◯ | 배열 | |
tuple | ◯ | 고정된 요소수 만큼의 타입을 미리 선언후 배열을 표현 | |
enum | ◯ | 열거형. 숫자값 집합에 이름을 지정한 것이다. | |
any | ◯ | 타입 추론(type inference)할 수 없거나 타입 체크가 필요없는 변수에 사용. var 키워드로 선언한 변수와 같이 어떤 타입의 값이라도 할당 가능. |
|
void | ◯ | 일반적으로 함수에서 반환값이 없을 경우 사용한다. | |
never | ◯ | 결코 발생하지 않는 값 |
string
let color: string = "blue";
color = 'red';
let myName: string = `Lee`; // ES6 템플릿 문자열
let greeting: string = `Hello, my name is ${ myName }.`; // ES6 템플릿 대입문
타입은 소문자(원시타입) & 대문자(객체타입)를 구분하기 때문에 주위가 필요
let primiteveStr: string = 'hello'; // OK
primiteveStr = new String('hello'); // Error - 원시 타입 문자열 타입에 객체를 할당하였다.
let objectStr: String = 'hello'; // OK
objectStr = new String('hello'); // OK
object
const obj: object = {};
// Date 타입
const today: Date = new Date();
// HTMLElement 타입
const elem: HTMLElement = document.getElementById('myId');
// Person 타입
class Person { }
const person: Person = new Person();
array
let list1: any[] = [1, 'two', true];
let list2: number[] = [1, 2, 3];
let list3: Array<number> = [1, 2, 3]; // 제네릭 배열 타입
tuple
배열을 자리를 고정시켜 안정성을 강화한 배열
let tuple: [string, number];
tuple = ['hello', 10]; // OK
tuple = [10, 'hello']; // Error
tuple = ['hello', 10, 'world', 100]; // Error
tuple.push(true); // Error
enum
인덱스 체계를 알고 있어야 할 듯
enum Color1 {Red, Green, Blue};
let c1: Color1 = Color1.Green;
console.log(c1); // 1
enum Color2 {Red = 1, Green, Blue};
let c2: Color2 = Color2.Green;
console.log(c2); // 2
enum Color3 {Red = 1, Green = 2, Blue = 4};
let c3: Color3 = Color3.Blue;
console.log(c3); // 4
any
let notSure: any = 4;
notSure = 'maybe a string instead';
notSure = false; // okay, definitely a boolean
void
function warnUser(): void {
console.log("This is my warning message");
}
never
function infiniteLoop(): never {
while (true) {}
}
function error(message: string): never {
throw new Error(message);
}
Static Typing (vs Dynamic Typing)
정적 타이핑(Static Typing)은 한번 명시적으로 선언한 타입을 바꿀 수 없는 방식
동적 타이핑(Dynamic Typing)은 느슨한 타입으로 타입 선언 없이 값이 할당되어 동적으로 타입을 추론(Type Inference)
js는 동적 타이핑 언어이나 ts는 정적 타이핑 언어입니다.
- javascript
var foo;
console.log(typeof foo); // undefined
foo = null;
console.log(typeof foo); // object
- typescript
let foo: string, // 문자열 타입
bar: number, // 숫자 타입
baz: boolean; // 불리언 타입
foo = 'Hello';
bar = 123;
baz = 'true'; // error: Type '"true"' is not assignable to type 'boolean'.
정적 타이팅의 장점은 코드 가독성, 예측성, 안정성의 향상이라고 볼 수 있는데 이는 대규모 프로젝트에 매우 적합하다.
Type Inference
타입 선언을 생략하면 값이 할당되는 과정에서 동적으로 타입이 결정되며 이를 타입 추론(Type Inference)이라 함
typescript는 동적 타이핑 언어이기 때문에 타입 추론을 하지 않고 타입을 명시적으로 나타냄
만약 타입을 생략하면 any
타입으로 설정되며 이는 typescript에서 권장하진 않음
Class
Properties
- ES6 Class
ES6 클래스는 몸체에 메소드만을 담을 수 있음
클래스 몸체에 프로퍼티를 선언할 수 없고 반드시 생성자 내부에서 클래스 프로퍼티를 선언하고 초기화
class Person {
constructor(name) {
// 클래스 프로퍼티의 선언과 초기화
this.name = name;
}
walk() {
console.log(`${this.name} is walking.`);
}
}
- typescript
class Person {
// 클래스 프로퍼티를 사전 선언하여야 한다
name: string;
constructor(name: string) {
// 클래스 프로퍼티수에 값을 할당
this.name = name;
}
walk() {
console.log(`${this.name} is walking.`);
}
}
const person = new Person('Lee');
person.walk(); // Lee is walking
Access Modifier
Typescript 클래스는 클래스 기반 객체 지향 언어가 지원하는 접근 제한자(Access Modifier)
public, private, protected를 지원하며 의미 또한 기본적으로 동일
접근 가능성 | public | protected | private |
---|---|---|---|
클래스 내부 | ◯ | ◯ | ◯ |
자식 클래스 내부 | ◯ | ◯ | ✕ |
클래스 인스턴스 | ◯ | ✕ | ✕ |
Constructor Access Modifier
접근 제한자는 생성자 파라미터에도 선언이 가능
생성자 파라미터에 접근 제한자 있을 때
이때 접근 제한자가 사용된 생성자 파라미터는 암묵적으로 클래스 프로퍼티로 선언되고
생성자 내부에서 별도의 초기화 없이 암묵적으로 초기화가 수행
class Foo {
/*
접근 제한자가 선언된 생성자 파라미터 x는 클래스 프로퍼티로 선언되고 지동으로 초기화된다.
public이 선언되었으므로 x는 클래스 외부에서도 참조가 가능하다.
*/
constructor(public x: string, private y: string) { }
}
const foo = new Foo('Hello', 'World');
console.log(foo); // Foo { x: 'Hello', y: 'World' }
console.log(foo.x); // Hello
console.log(foo.y); // private이 선언된 bar.x는 클래스 내부에서만 참조 가능하다
생성자 파라미터에 접근 제한자 없을 때
만일 생성자 파라미터에 접근 제한자를 선언하지 않으면
생성자 파라미터는 생성자 내부에서만 유효한 지역 변수가 되어 생성자 외부에서 참조가 불가능하게 된다.
class Foo {
// x는 생성자 내부에서만 유효한 지역 변수이다.
constructor(x: string) {
console.log(x);
}
}
const foo = new Foo('Hello');
console.log(foo); // Foo {}
readonly
readonly가 선언된 클래스 프로퍼티는 선언 시 또는 생성자 내부에서만 값을 할당
그 외의 경우에는 값을 할당할 수 없고 오직 읽기만 가능한 상태가 된다.
이를 이용하여 상수의 선언에 사용한다. (전역적인 상수라 할 순 없고 클래스 내부의 상수)
class Foo {
private readonly MAX_LEN: number = 5;
private readonly MSG: string;
constructor() {
this.MSG = 'hello';
}
log() {
// readonly가 선언된 프로퍼티는 재할당이 금지된다.
this.MAX_LEN = 10; // Cannot assign to 'MAX_LEN' because it is a constant or a read-only property.
this.MSG = 'Hi'; // Cannot assign to 'MSG' because it is a constant or a read-only property.
console.log(`MAX_LEN: ${this.MAX_LEN}`); // MAX_LEN: 5
console.log(`MSG: ${this.MSG}`); // MSG: hello
}
}
new Foo().log();
static
ES6 클래스에서 static 키워드는 클래스의 정적 메소드를 정의
정적 메소드는 클래스의 인스턴스가 아닌 클래스 이름으로 호출
typescript에선 클래스 프로퍼티에서 static 사용이 가능
class Foo {
// 생성된 인스턴스의 갯수
static instanceCounter = 0;
constructor() {
// 생성자가 호출될 때마다 카운터를 1씩 증가시킨다.
Foo.instanceCounter++;
}
}
var foo1 = new Foo();
var foo2 = new Foo();
console.log(Foo.instanceCounter); // 2
console.log(foo2.instanceCounter);
abstract
추상 클래스(abstract class)는 하나 이상의 추상 메소드를 포함하며 일반 메소드도 포함이 가능
추상 메소드는 내용이 없이 메소드 이름과 타입만이 선언된 메소드를 말하고 선언할 때 abstract 키워드 사용
abstract class Animal {
// 추상 메소드
abstract makeSound(): void;
// 일반 메소드
move(): void {
console.log('roaming the earth...');
}
}
// 직접 인스턴스를 생성할 수 없다.
// new Animal();
// error TS2511: Cannot create an instance of the abstract class 'Animal'.
class Dog extends Animal {
// 추상 클래스를 상속한 클래스는 추상 클래스의 추상 메소드를 반드시 구현하여야 한다
makeSound() {
console.log('bowwow~~');
}
}
const myDog = new Dog();
myDog.makeSound();
myDog.move();
Interface
인터페이스는 개발 단계에서 도움을 주기 위해 제공되는 기능으로 자바스크립트의 표준이 아니다.
따라서TypeScript 파일을 자바스크립트 파일로 트랜스파일링하면 인터페이스가 삭제된다.
Object & Interface
// 인터페이스의 정의
interface Todo {
id: number;
content: string;
completed: boolean;
}
// 변수 todo의 타입으로 Todo 인터페이스를 선언하였다.
let todo: Todo;
// 변수 todo는 Todo 인터페이스를 준수하여야 한다.
todo = { id: 1, content: 'typescript', completed: false };
Array & Interface
// 인터페이스의 정의
interface Todo {
id: number;
content: string;
completed: boolean;
}
let todos: Todo[] = [];
// 파라미터 todo의 타입으로 Todo 인터페이스를 선언하였다.
function addTodo(todo: Todo) {
todos = [...todos, todo];
}
// 파라미터 todo는 Todo 인터페이스를 준수하여야 한다.
const newTodo: Todo = { id: 1, content: 'typescript', completed: false };
addTodo(newTodo);
console.log(todos)
// [ { id: 1, content: 'typescript', completed: false } ]
Function & Interface
인터페이스를 함수의 타입으로 사용 가능
// 함수 인터페이스의 정의
interface SquareFunc {
(num: number): number;
}
// 함수 인테페이스를 구현하는 함수는 인터페이스를 준수하여야 한다.
const squareFunc: SquareFunc = function (num: number) {
return num * num;
}
console.log(squareFunc(10)); // 100
Class & Interface
typeA (properties)
// 인터페이스의 정의
interface ITodo {
id: number;
content: string;
completed: boolean;
}
// Todo 클래스는 ITodo 인터페이스를 구현하여야 한다.
class Todo implements ITodo {
constructor (
public id: number,
public content: string,
public completed: boolean
) {}
}
const todo = new Todo(1, 'Typescript', false);
console.log(todo);
typeB (properties + method)
// 인터페이스의 정의
interface IPerson {
name: string;
sayHello(): void;
}
/*
인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메소드를 반드시 구현하여야 한다.
*/
class Person implements IPerson {
// 인터페이스에서 정의한 프로퍼티의 구현
constructor(public name: string) {}
// 인터페이스에서 정의한 추상 메소드의 구현
sayHello() {
console.log(`Hello ${this.name}`);
}
}
function greeter(person: IPerson): void {
person.sayHello();
}
const me = new Person('Lee');
greeter(me); // Hello Lee
DuckTyping
주의할 것은 인터페이스 구현이 타입 체크 통과의 유일한 방법은 아니라는 점
타입 체크에서 중요한 것은 값을 실제로 가지고 있다는 것
Typescript는 해당 인터페이스에서 정의한 프로퍼티나 메소드를 가지고 있다면, 그 인터페이스를 구현한 것으로 인정
이것을 덕 타이핑(Duck Typing) 또는 구조적 타이핑(Structural Typing)이라 명명
interface IDuck { // 1
quack(): void;
}
class MallardDuck implements IDuck { // 3
quack() {
console.log('Quack!');
}
}
class RedheadDuck { // 4
quack() {
console.log('q~uack!');
}
}
function makeNoise(duck: IDuck): void { // 2
duck.quack();
}
makeNoise(new MallardDuck()); // Quack!
makeNoise(new RedheadDuck()); // q~uack! // 5
선택적 프로퍼티
인터페이스 프로퍼티는 반드시 구현되어야 하나 인터페이스의 프로퍼티가 선택적으로 필요한 경우가 있을 수 있음
이 때 선택적 프로퍼티(Optional Property)를는 프로퍼티명 뒤에 ?
를 붙여 생략하여도 에러가 발생하지 않음
interface UserInfo {
username: string;
password: string;
age? : number;
address?: string;
}
const userInfo: UserInfo = {
username: 'ungmo2@gmail.com',
password: '123456'
}
console.log(userInfo);
Generic
제네릭은 선언 시점이 아닌 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법
한 번은 선언으로 다양한 타입 재사용이 가능하다는 장점
(T는 제네릭을 선언할 때 관용적으로 사용되는 식별자로 타입 파라미터)
Class & Generic
class Queue<T> {
protected data: Array<T> = [];
push(item: T) {
this.data.push(item);
}
pop(): T {
return this.data.shift();
}
}
Function & Generic
함수에 전달되는 타입에 따라 매개변수 타입이 달라집니다.
만약 {name: string} 타입의 요소를 갖는 배열을 전달받으면 타입 매개변수는 {name: string}가 된다.
function reverse<T>(items: T[]): T[] {
return items.reverse();
}
'FrontEnd > TypeScript' 카테고리의 다른 글
[TS] modules (0) | 2023.03.31 |
---|