리덕스를 배우고 나서 타입스크립트를 배우려한다
타입스크립트를 알고나면 자바스크립트가 너무너무 불편하다고 하는데 한번 체험해보자 ..!
타입스크립트를 사용하여 개발하면 많은 실수를 줄일 수 있다
function sum(a: number, b: number) {
return a + b;
}
위와 같이 타입스크립트로 sum이라는 함수를 만들고
sum(10, 20); // 30
sum('10', '20'); // error
위 코드처럼 함수를 호출했을 때, 첫번째 호출은 호출 인자로 number가 들어가서 두 숫자를 합한 값을 리턴받지만
두번째 호출은 string 타입이 들어갔기 때문에 오류가 나게 된다
이밖에도 이상한 오타를 낸 경우, 다른 타입끼리 비교를 한 경우, null체킹을 빠뜨린 경우와 같은 상황에도 TypeScript를 사용하면 코드를 실행하기 전에 에디터에서 바로 에러를 줄일 수 있다
타입스크립트는 자동완성 및 타입 체킹이 되기 때문에 개발 생산성이 정말 높아진다.
함수에 어떤 파라미터가 들어가야 하는지, 리액트 개발할 때 props 에 어떤 값이 들어가야 하는지 쉽게 알 수 있다.
또, 리덕스를 사용할 때, 리듀서 코드를 열어보지 않고 상태 객체가 어떤 구조로 이루어 졌는지 알 수 있다.
먼저 새로운 타입스크립트 프로젝트를 생성한다
$ mkdir ts-tutorial
$ cd ts-tutorial
$ yarn init -y # 또는 npm init -y
프로젝트를 생성한 이후 타입스크립트를 글로벌로 설치해준다
$ yarn global add typescript # 또는 npm install -g typescript
타입스크립트를 설치한 이후 프로젝트의 디렉토리에서 다음 명령어를 입력하면 tsconfig.json 파일이 생성된다.
$ tsc --init
tsconfig.json 파일은 아래와 같이 설정해준다
{
"compilerOptions": {
"module": "commonjs", // nodejs를 평범하게 사용하게 하면서 다양한것을 import 또는 export 할 수 있게 함
"target": "es5", // 어떤 버전의 JS로 컴파일 되는지 지정
"sourceMap": true,
"outDir": "./dist", // // ts -> js 로 컴파일된 파일을 dist디렉토리에 저장
"strict": true,
"esModuleInterop": true
},
"include": ["./src/**/*"], //ts 파일은 모두 src 디렉토리에 있다는 것을 명시
"exclude": ["node_modules"]
}
타입스크립트 컴파일 명령어 tsc 를 사용하여 타입스크립트 파일을 자바스크립트 파일로 변환한다
특정 파일만 컴파일하는 명령어는 tsc app.ts
변환된 자바스크립트 파일은 /dist 디렉터리에 옮겨지게 된다

위의 구성처럼 src 디렉토리에는 ts파일이 있고, dist 디렉토리에는 Js 파일이 들어가게 된다.
let str: string = 'hi';
자바스크립트 변수의 타입이 문자열인 경우 위와 같이 선언해서 사용한다.
let num: number = 10;
타입이 숫자이면 위와 같이 선언한다.
let isLoggedIn: boolean = false;
타입이 Boolean인 경우에는 위와 같이 선언한다.
let arr: number[] = [1, 2, 3];
타입이 배열인 경우 위와같이 선언한다.
let arr: Array<number> = [1, 2, 3];
위처럼 Generic을 이용하여 선언할 수도 있다.
enum Avengers {
Capt,
IronMan,
Thor,
}
let hero1: Avengers = Avengers.Capt;
Enum은 특정 값(상수)들의 집합을 의미한다.
enum Avengers {
Capt,
IronMan,
Thor,
}
let hero: Avengers = Avengers[0];
이넘은 인덱스 번호로도 접근할 수 있다.
let strAny: any = 'hi';
let numAny: any = 10;
let arrAny: any = ['a', 2, true];
기존에 자바스크립트로 된 코드를 타입스크립트로 변환할 때 활용하면 좋다.
단어 의미 그대로 모든 타입에 대해서 허용한다는 의미를 갖고 있다.
let unuseful: void = undefined;
function notuse(): void {
console.log('sth');
}
변수에는 undefined와 null만 할당하고, 함수에는 반환 값을 설정할 수 없는 타입이다
C언어의 Void 함수와 같은 개념인 것 같다.
유니온 타입(Union Type)이란 자바스크립트의 OR 연산자(||)와 같이 A이거나 B이다 라는 의미의 타입이다
let name: string | undefined = undefined; // string 일수도 있고 undefined 일수도 있음
let age: number | null = null; // number 일수도 있고 null 일수도 있음
let color: 'red' | 'orange' | 'yellow' = 'red'; // red, orange, yellow 중 하나임
color = 'yellow';
color = 'green'; // 에러 발생!
위 코드를 보면 name 변수의 값은 String값이 들어올 수도 있고 undefined가 들어 올 수도 있다 age는 number일 수도, null일 수도 있는 것이다
변수 color의 경우에도 red, orange, yellow의 값중 하나의 값만 들어올 수 있는데 green이라는 값이 들어와 에러가 발생한다
타입스크립트의 함수 선언 방법의 이해를 위해 자바스크립트 코드와 비교해보자
//js
function sum(a, b) {
return a + b;
}
//ts
function sum(a: number, b: number): number {
return a + b;
}
기존 자바스크립트의 함수와는 다르게 파라미터와 리턴값에 어떤 값이 들어갈지 명시해준다.
리턴을 하지않는 함수를 정의할 때는 Void 를 사용하면 된다
타입스크립트에서는 함수의 인자는 필수 값으로 간주한다 즉 undefined나 null이라도 인자로 넘겨야하며
함수를 호출할 때 인자가 적거나 많아서는 안 된다
아래 코드를 통해 살펴보자
function sum(a: number, b: number): number {
return a + b;
}
sum(10, 20); // 30
sum(10, 20, 30); // error, too many parameters
sum(10); // error, too few parameters
위의 코드처럼 첫번째 호출은 함수의 인자의 개수에 맞게 호출 했지만 두번째와 세번째는 함수의 인자 개수에 맞지않아 오류가 발생한다.
이를 해결하기 위해 매개변수의 갯수만큼 인자를 넘기지 않아도 되는 JS의 특성을 살리고 싶다면 ?를 이용하면 된다
아래 코드를 통해 살펴보자
function sum(a: number, b?: number): number {
return a + b;
}
sum(10, 20); // 30
sum(10, 20, 30); // error, too many parameters
sum(10); // 10
위의 코드처럼 매개변수 b에 ?를 추가해줌으로써 인자의 개수에 맞지 않게 호출해도 오류가 발생하지 않는다
단, 매개변수의 개수를 초과한 호출은 오류가 발생한다.
인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다
let person = { name: 'Song', age: 26 };
function logAge(obj: { age: number }) {
console.log(obj.age); // 26
}
logAge(person); // 26
위 logAge() 함수에서 받는 인자의 형태는 age를 속성으로 갖는 객체이다.
이렇게 인자를 받을 때 단순한 타입 뿐만 아니라 객체의 속성 타입까지 정의할 수 있다.
객체를 생성할 때 interface를 사용하여 생성하는 코드를 살펴보자
interface personInfo {
name: string;
age?: number;
}
interface animalInfo {
name: string;
age?: number;
kind: string;
}
function logAge(obj: personInfo) {
console.log(obj.age);
}
let person = { name: 'Song', age: 26, job: 'frontend' };
logAge(person);
const expert: animalInfo = {
name: '똘이',
kind: 'dog',
};
위 코드를 보고 알 수 있는 것은 인터페이스르 인자로 받아 사용할 때 항상 인터페이스의 속성 갯수와 인자로 받는 객체의 속성 갯수를 일치 시키지 않아도 된다는 것이다.
인터페이스에 정의된 속성과 타입만 만족한다면 객체의 속성 갯수가 더 많아도 상관 없다.
personInfo의 interface의 속성과 animalInfo의 속성의 형태가 유사하다. 이 경우에는 interface 를 선언 할 때 다른 interface 를 extends 해서 상속받을 수 있다.
아래의 코드로 살펴보자
interface personInfo {
name: string;
age?: number;
}
interface animalInfo extends personInfo {
kind: string;
}
function logAge(obj: personInfo) {
console.log(obj.age);
}
let person = { name: 'Song', age: 26, job: 'frontend' };
logAge(person);
const expert: animalInfo = {
name: '똘이',
kind: 'dog',
};
animalInfo 인터페이스가 personInfo 인터페이스의 값을 상속 받았기 때문에 personInfo의 속성을 사용할 수 있는 것이다
제네릭은 C#, Java 등의 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징이다. 특히, 한가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트를 생성하는데 사용된다.
function logText<T>(text: T): T {
return text;
}
// #1 가독성 낮음
const text = logText<string>('Hello Generic');
// #2 가독성 높아서 자주 사용됨
const text = logText('Hello Generic');
//만약 복잡한 코드에서 두 번째 코드로 타입 추정이 되지 않는다면 첫 번째 방법을 사용
console.log(text);
위의 코드를 보면 logText 함수를 제네릭으로 선언하였는데 제네릭은 어떤 타입이 들어와도 동작을 하게됩니다.
Any와 유사하다고 할 수 있는데 Any와 차이점으로는 Any는 함수의 인자로 어떤 타입이 들어갔고 어떤 값이 반환 되는지 알 수 없지만 제네릭은 함수를 호출할 때 넘긴 타입에 대해 타입스크립트가 추정할 수 있게 된다.
type은 특정 타입에 별칭을 붙이는 용도로 사용된다. 이를 사용하여 객체를 위한 타입을 설정할 수도 있고, 배열, 또는 그 어떤 타입이던 별칭을 지어줄 수 있다
아래 코드로 살펴보자
type personInfo {
name: string;
age?: number;
}
const person: personInfo = {
name: '홍길동'
};
사용하는 방법은 인터페이스와 유사하나 인터페이스와 차이점은 타입의 확장 가능, 불가능 여부이다.
인터페이스는 확장이 가능하지만 타입 별칭은 불가능하다 따라서, type 보다는 interface로 선언해서 사용하도록 하자
자바스크립트 핸드북
https://joshua1988.github.io/ts/
타입스크립트 기초 연습