코딩애플 및 Velopert 님께서 작성한 TypeScript 내용을 정리해보았다.
TypeScript 사용이유
1. IDE를 더욱 더 적극적으로 활용 (자동완성, 타입확인)
TypeScript에서는 자동완성의 기능을 잘 활용할 수 있다. 해당 함수가 어떤 파라미터를 필요로 하는지, 그리고 어떤 값을 반환하는지 코드를 따로 열어보지 않아도 알 수 있다.
리액트 컴포넌트의 경우 props에는 무엇을 전달해주어야 하는지 쉽게 알 수 있고, 컴포넌트 내부에는 props와 state에 어떤 값이 있는지 확인이 가능하다.
또한, 리덕스를 사용할 경우에도 스토어 안에 어떤 상태가 들어있는지 바로 조회가 가능하다.
2. 실수방지
코드를 실행하지 않고도 IDE 상에서 바로 알 수 있다. null 혹은 undefined 일 수도 있는 값의 내부 값 혹은 함수를 호출한다면, 사전에 null 체킹을 하지 않으면 오류가 뜨므로 null 체킹도 확실하게 할 수 있게 된다.
TypeScript 설치
- nodejs 설치
- VScode 에디터 준비
- Terminal 오픈 후,
npm install -g typescriptyarn을 사용한다면,
yarn global add typescript위 코드 입력하기
4. 에디터에 프로젝트 폴더 open
5. 어쩌구.ts 파일 생성 후 코드작성
6. tsconfig.json 생성
tsc --init 명령어를 입력하면 tsconfig.json파일이 자동생성된다.
{
"compilerOptions" : {
"target": "es5",
"module": "commonjs",
}
}위 코드 입력하기
ts 파일은 브라우저에서 인식을 못함!
-> 그래서 js 파일로 변환하는 작업이 필요하다.
터미널을 연다음,
tsc -w위 코드 입력하면 ts파일을 js파일로 변환해준다.
그리고, compilerOptions에서 js 버전 등을 세팅가능하다.
tsconfig.json에서의 용어정리를 몇 개 해보았다.
-
target: 컴파일된 코드가
어떤 환경에서 실행될지 정의한다. es5에서는 화살표 함수를 사용하면, 일반 function 키워드를 사용하는 함수로 변환을 해주지만, es6에서는 화살표 함수를 그대로 유지해준다. -
module: 컴파일된 코드가
어떤 모듈 시스템을 사용할지 정의한다. common으로 세팅으로 하면export default Sample을 하게 됐을때 컴파일 된 코드에서는exports.default = helloWorld로 변환해주지만 이 값을 es2015로 하면export default Sample구문을 그대로 유지해준다. -
strict: 모든 타입 체킹 옵션을 활성화한다는 의미이다.
-
esModuleInterop: commonjs 모듈 형태로 이루어진 파일을 es2015 모듈 형태로 불러올 수 있게 해준다.
outDir 속성을 설정해주면 컴파일된 파일들이 저장되는 경로를 지정할 수 있다.
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist"
}
}문법정리
변수에 타입지정
let name: string = 'kim'
name = 123 // errorarray 타입지정
let arr: string[] = ['kim', 'park'] // arr에는 string타입 변수만 들어올 수 있다.
let obj: { name: string } = { name: 'kim' }
let obj2: { name?: string } = { name: 'kim' } // name이라는 속성이 들어올 수도 안 들어올 수 있다라고 의미Union type: 다양한 타입이 들어올 수 있게 하는 타입
let name: string | number = 'kim'
let name: string | number = 123
// 둘다 가능Type alias: 타입은 변수에 담아쓸 수 있다.
type MyType = string | number
let name: MyType = 123 // string 혹은 number가 name에 올 수 있다.
type Person = {
name: string
age?: number // 물음표가 들어갔다는 것은, 설정을 해도 되고 안해도 되는 값이라는 것을 의미
}
// & 는 Intersection 으로서 두개 이상의 타입들을 합쳐준다.
// 참고: https://www.typescriptlang.org/docs/handbook/advanced-types.html#intersection-types
type Developer = Person & {
skills: string[]
}
const person: Person = {
name: '김사람',
}
const expert: Developer = {
name: '김개발',
skills: ['javascript', 'react'],
}
type People = Person[] // Person[] 를 이제 앞으로 People 이라는 타입으로 사용 할 수 있다.
const people: People = [person, expert]
type Color = 'red' | 'orange' | 'yellow'
const color: Color = 'red'
const colors: Color[] = ['red', 'orange']함수에 타입 지정가능
function fn(x: number): number {
return x * 2
}매개변수 및 return 값에 타입 지정 가능하다.
array에 쓸 수 있는 tuple 타입
type Member = [number, boolean]
let john: Member = [123, true]
let lee: Member = ['123', true] // errorobject에 타입 지정해야할 속성이 너무 많을 경우
type Member = {
[key: string]: string // 글자로 된 모든 object 키는 string 값을 가져야 한다
}
let john: Member = { name: 'john', age: '20' }일일이 name, age 키의 타입을 지정할 필요가 없다
class 타입지정 가능
class User {
name: string
constructor(name: string) {
this.name = name
}
}interface 사용해보기
interface는 클래스 또는 객체를 위한 타입을 지정할 때 사용되는 문법이다.
클래스에서 interface를 implements 하기
// Shape 라는 interface 를 선언
interface Shape {
getArea(): number // Shape interface 에는 getArea 라는 함수가 꼭 있어야 하며 해당 함수의 반환값은 숫자이다.
}
class Circle implements Shape {
// `implements` 키워드를 사용하여 해당 클래스가 Shape interface 의 조건을 충족하겠다는 것을 명시
constructor(public radius: number) {
this.radius = radius
}
// 너비를 가져오는 함수를 구현
getArea() {
return this.radius * this.radius * Math.PI
}
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {
this.width = width
this.height = height
}
getArea() {
return this.width * this.height
}
}
const circle = new Circle(5)
const rectangle = new Rectangle(10, 5)
console.log(circle.radius)
console.log(rectangle.width)
const shapes: Shape[] = [new Circle(5), new Rectangle(10, 5)]
shapes.forEach(shape => {
console.log(shape.getArea())
})타입스크립트에서는 constructor의 파라미터 쪽에 public 또는 private 접근자를 사용하면 직접 하나하나 설정해주는 작업을 생략해줄 수 있다. 다시말해, 밑 코드와 같은 부분을 윗 코드처럼 대신 작성이 가능하다는 것 이다.
width: number;
height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}일반 객체를 interface 로 타입 설정하기
interface Person {
name: string
age?: number // 물음표가 들어갔다는 것은, 설정을 해도 되고 안해도 되는 값이라는 것을 의미
}
interface Developer {
name: string
age?: number
skills: string[]
}
const person: Person = {
name: '김사람',
age: 20,
}
const expert: Developer = {
name: '김개발',
skills: ['javascript', 'react'],
}윗 코드를 보면 Person과 Developer가 형태가 유사한데 이럴 땐 interface가 다른 interface를 상속할 수 있도록 extends 키워드를 사용 가능하다.
interface Person {
name: string
age?: number // 물음표가 들어갔다는 것은, 설정을 해도 되고 안해도 되는 값이라는 것을 의미
}
interface Developer extends Person {
skills: string[]
}
const person: Person = {
name: '김사람',
age: 20,
}
const expert: Developer = {
name: '김개발',
skills: ['javascript', 'react'],
}
const people: Person[] = [person, expert]클래스와 관련된 타입의 경우엔 interface 를 사용하는게 좋고, 일반 객체의 타입의 경우엔 그냥 type 을 사용해도 무방하다고 한다.
Generics
제네릭은 타입스크립트에서 함수, 클래스, interface, type alias를 사용하게 될 때 여러 종류의 타입에 대하여 호환을 맞춰야 하는 상황에서 사용하는 문법이다.
함수에 Generics 사용하기
객체 A와 객체 B를 합쳐주는 merge 함수를 구현한다고 가정하자.
function merge(a: any, b: any): any {
return {
...a,
...b,
}
}
const merged = merge({ foo: 1 }, { bar: 1 })A와 B가 어떤 타입이 올 지 모르기 때문에 이런 상황에서는 any라는 타입을 윗 코드처럼 작성해도 되지만, 그렇게되면 merged 역시 any 타입이 되어버리기 때문에 안에 무엇이 있는지 알 수 없다.
이런 상황일 때 Generics 를 사용하면 된다.
function merge<A, B>(a: A, b: B): A & B {
return {
...a,
...b,
}
}
const merged = merge({ foo: 1 }, { bar: 1 })또 다른 예시 코드로 다음을 들 수 있다.
function wrap<T>(param: T) {
return {
param,
}
}
const wrapped = wrap(10)interface에서 Generics 사용하기
이번엔 interface에서 Generics를 사용하는 방법을 알아보았다.
interface Items<T> {
list: T[]
}
const items: Items<string> = {
list: ['a', 'b', 'c'],
}type 에서 Generics 사용하기
interface 에서 Generics를 사용한 것과 매우 유사하다.
type Items<T> = {
list: T[]
}
const items: Items<string> = {
list: ['a', 'b', 'c'],
}클래스에서 Generics 사용하기
Queue 라는 자료구조를 구현한 클래스이다. 밑 코드를 통해 클래스에서는 어떻게 Generics를 적용할 수 있는지 확인할 수 있다.
class Queue<T> {
list: T[] = []
get length() {
return this.list.length
}
enqueue(item: T) {
this.list.push(item)
}
dequeue() {
return this.list.shift()
}
}
const queue = new Queue<number>()