typescript, react, react-dom, webpack, webpack-cli 설치
1 2 3 4 5
$ npm i typescript # 웹 환경에서는 react-dom, 모바일은 react-native $ npm i react react-dom
$ npm i -D webpack webpack-cli
이전에 React에서 webpack + babel을 사용해서 JavaScript의 최신문법과 JSX 문법을 이전의 JavaScript 문법으로 변환해주었는데, 이번 TypeScript에서는 webpack + TypeScript 조합으로 변환을 해준다.
TypeScript는 자체적으로 babel처럼 최신문법이나 jsx 문법을 이전 문법(ES3 or ES5)으로 바꿔준다. 따라서 별도로 Babel을 사용할 필요는 없다. 하지만 TypeScript + Babel의 조합으로 사용하는 경우도 있기 때문에 이 부분을 알아두도록 하자.
만약에 사용하고자 하는 package가 기본적으로 type을 제공하지 않고, DefinitelyTyped에도 없는 경우, 이런 경우에는 내가 직접 typing을 해줘야 한다. (전부 typing하지 않고 내가 사용할 부분만 부분 typing한다.)
예를들어, can-use-dom이라는 타입을 제공하지 않는 패키지를 설치해서 사용하는데, DefinitelyTyped에도 없는 경우, 이 경우에는 아래와 같이 프로젝트 폴더 안에 *.d.ts파일을 작성해줘야 한다. 되도록이면 types와 같은 별도 폴더를 생성해서 내부에 *.d.ts파일을 작성해주도록 한다.
이제 작성해준 can-use-dom.d.ts파일을 *.ts 파일 내에서 인식시키기 위해서 tsconfig.json파일에서 "typeRoot"옵션의 속성값으로 작성해준 *.d.ts파일의 폴더 경로를 적어줘야 한다.
tsconfig.json
1 2 3 4 5 6 7 8 9 10 11 12
{ "compilerOptions": { ... "typeRoots": ["./types", "./node_modules/@types"], ... }, "exclude": ["*.js"], // "include"에는 구체적으로 특정 파일을 작성하지 않도록 한다. // 만약 구체적으로 파일을 명시하게 되면 해당 타입스크립트 파일만 컴파일되기 때문에 // 내가 작성한 *.d.ts 파일이 컴파일 되지 않는다. "include": [], }
함수의 매개변수가 단순한 경우에는 그냥 각 각의 인수를 넣어서 호출해줘도 되지만, 만약에 함수의 매개변수로 넘겨줘야 되는 인수의 갯수가 많아지는 경우, 각 각의 인수가 어떤 역할을 하는지 파악하려면 다시 그 함수를 참조해야 한다. 따라서 넘겨줘야 되는 인수의 갯수가 늘어나는 경우 객체 타입으로 작성해서 각 인수의 역할에 대해서 유추할 수 있도록 작성을 해주면 코드의 가독성이 높아진다.
1
Example({ flag: true, id: '1' });
함수의 구조분해 매개변수에 타입 지정해주기
구조분해한 매개변수의 경우에는 구조분해한 변수의 뒤에 타입을 지정하지 않고 구조분해한 {}뒤에 콜론을 찍고 타입을 지정해준다.
TypeScript는 HTML 자체를 인식하지 못하기 때문에 HTML 내의 요소를 참조할때 null로 인식하는 경우가 있다.
따라서 확실하게 HTML에 요소가 존재하는 경우에는 개발자가 !로 존재를 보증해주거나 if 문으로 조건처리를 해서 작성을 해줘야 한다.
1 2 3
if (document.querySelector('#id')) { (document.querySelector('#id') as HTMLElement).style.background = '......'; }
TypeScript에서 inline styling
타입스크립트에서 inline 스타일링을 하기 위해 HTML element를 참조하게 되면 .style을 하거나 .textContent를 하는 경우에 에러가 발생한다. 그 이유는 아래와 같이 querySelector로 HTML 요소를 참조하게 되면 해당 참조 요소의 타입은 Element를 상속받는 제네릭 타입 E가 된다. 이 Element에는 style, textContent 속성이 없다.
1
querySelector<E extends Element = Element>(selectors: string): E | null;
따라서 Element type을 HTMLDivElement로 타입의 범위를 좁게 잡아줘야 한다.
// as로 Type casting (document.querySelector('#id') as HTMLDivElement).style.background = '...';
// generic으로 Type casting document.querySelector<HTMLDivElement>('#id').style.background = '...';
tsc로 타입스크립트 파일을 컴파일할때에는 파일명을 넣지 않는다.
타입스크립트 파일을 컴파일할때 npx tsc -w과 같이 컴파일할 파일을 명시하지 않도록 한다. 그 이유는 파일명을 구체적으로 명시해주게 되면, 프로젝트 폴더 내에 정의한 타입스크립트 설정파일(tsconfig.json)이 무시가 되서 설정된 사항들이 적용이 안될 수 있기 때문이다.
비슷한 속성의 객체는 같은 interface로 타입을 정의할 수 있다.
타입스크립트의 장점은 interface에 작성해준 타입의 정의를 보고 프로그램의 구조에 대한 힌트를 얻을 수 있다.
아래에 사람(Person)과 레스토랑(Restaurant) 인터페이스를 작성해보았다. 아래 예시 코드를 통해 "strictNullCheck": true일 경우의 null과 undefined에 대한 내용도 한 번 정리해보겠다.
객체를 as const로 정의를 해주게 되면 객체내의 모든 타입 속성들이 readonly로 바뀌는 것을 확인할 수 있다. 만약 일부의 타입 속성들만 상수로써 바뀌지 않게 하고 싶다면 해당 타입 속성의 변수명의 앞에 readonly를 붙여주도록 한다. 만약 변수의 타입이 상수 타입으로 고정되는 경우, 이 경우에는 별도로 as const나 readonly를 붙여주지 않아도 상관없다.
interface와 type alias 비교 및 사용
interface와 type alias를 사용해서 객체의 타입을 정의할때 그 용도를 일관되게 나눠서 사용하는 것이 좋다. 예를들어, 객체의 타입은 interface를 사용해서 정의하고 custom type은 type alias를 사용해서 정의하도록 하는 것이다. interface와 type 모두 아래와 같이 객체의 타입을 정의할 수는 있지만 코드를 작성할때 일관성을 지켜서 코드를 작성하는 편이 좋다.
interface의 특징
interface내에 정의한 타입 속성은 ,(콤마)나 ;(세미콜론), \n(줄바꿈)으로 구분할 수 있다.
Typescript는 언어이자 라이브러리로, 프로그래밍 언어는 실행기가 필요하다. JavaScript의 실행기는 ‘Browser’와 ‘Node’가 있다. 이 실행기가 프로그래밍 언어를 해석해주는 역할을 해준다. 그렇다면 TypeScript의 실행기는 무엇일까? TypeScript의 실행기는 현재 개발중에 있는 Deno이다. 아직은 안정적인 버전이아니기 때문에 라이브러리가 TypeScript의 실행기이다. 실행기가 라이브러리라서 좋은 점은 빠르게 release할 수 있다는 점이 있지만 단점으로는 여러가지 버그가 존재한다는 것이다. TypeScript의 라이브러리(실행기)를 설치하기 위해서는 Node를 설치해줘야 되기 때문에 Node에 대해서 어느정도는 이해해야 한다. 이 TypeScript 라이브러리가 TypeScript를 JavaScript로 코드 변환을 해주는데 이는 브라우저는 JavaScript 코드만 이해할 수 있기 때문이다.