[Node.js 교과서] 17-1. 타입스크립트 기본 문법
TypeScript (TS)
타입스크립트 (TS)는 자바스크립트 (JS)에 명시적으로 타입이 추가된 언어임
JS에도 문자열, 숫자, 불린, 객체 같은 자료형 타입이 있음
그저 JS 코드를 작성할 때, 명시적으로 타입을 지정하지 않을 뿐!
TS 코드는 tsc라는 컴파일러를 통해 JS 코드로 변화할 수 있음
Node JS는 JS만 실행할 수 있으므로, TS 코드를 JS 코드로 변환해야만 실행 가능함
디노 (deno)라는 TS를 실행할 수 있는 런타임이 있긴 하지만 아직 Node JS보다 대중적이지 않아서 많이 사용 안함
- tsc 설치 방법
npm i typescript
//js 프로젝트를 ts 프로젝트로 변환 할 수 있음
npx tsc --init
//ts config가 생김
- tsconfig.json
{
"compilerOptions": { /* 타입스크립트 컴파일러인 tsc에 대한 설정*/
/* Language and Environment : 어떤 버전의 자바스크립트를 바꿀건지 */
"target": "es2016",
/* Modules : 브라우저의 모듈은 es2022, nodejs는 commonjs, 이 둘은 완벽히 호환이 안됨 */
"module": "commonjs",
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
/* common js 모듈을 es 모듈처럼 해석 할 수 있게 도와주는 옵션 */
"esModuleInterop": true,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* import 시, 대소문자 구분 Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true,
/* Completeness 이 옵션을 false로 하면 node module에 대해서도 전부 타입 검사를 실시해서 느려짐*/
/* 라이브러리에 대해 타입 체크를 true 하는 것이 좋음 */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true
}
}
ts 파일을 실행시키려면 js로 변환해야하는데 terminal에서 npx tsc를 실행하면 됨
//ts 파일을 변환하여 js 파일이 생성
npx tsc
//ts 파일에 대한 type 검사만 실행되고 js 파일은 생성되지 않음
npx tsc --noEmit
그러면 아래처럼 test.ts 파일이 test.js로 컴파일 됨
tsc 컴파일러를 사용할 때,
아래와 같이 문자열 type 변수인 a에 숫자를 넣으면, js 변환 시 에러가 발생하지만 변환 자체는 됨
그 이유는 tsc는 타입 검사와 js 컴파일 두가지를 모두 해야하기 때문에
에러가 있어도 js 변환 자체는 됨
타입 선언하기
any로 선언하면 그냥 js를 사용하는 것과 마찬가지가 되어서
최대한 any는 지양하자!
//변수, 매개변수, 리턴값에 타입을 붙임
// node js는 타입스크립트를 바로 실행할 수 없음, 타입스크립트는 tsc라는 컴팡리러를 통해 자바스크립트 코드로 변환할 수 있음
let a:boolean = true;
const b:{hello:string} = {hello:'world'}
function add(x:number, y:number) :number {return x+y};
const minus = (x:number, y:number):number => x-y;
const func:(x:number)=>string = (x:number) => x.toString(); //화살표 함수에 대해서는 타입 명시 부분이 가독성이 안좋음
//아래와 같이 함수에 대한 매개변수와 return값 type을 먼저 선언하고
type Func2 = (x:number) => string;
//함수 내용 선언하여 가독성을 좋게 할 수 있음
const func2:Func2 = (x:number) => x.toString();
// type 선언을 안하면 any로 선언하는 것과 같음
function add2(x :any,y : any) {return x+y}
//function add2(x,y) {return x+y}
하지만 모든 것에 type을 명시하는 것은 휴먼 에러를 불러올 수 있음!
그렇기 때문에 타입스크립트의 type 추론을 적극 활용하자
Type 추론
// b 객체의 hello에 대해 type 명시
const b:{hello:string} = {hello:'world'}
// 아래는 hello 값을 보고서 타입스크립트가 hello의 값은 string이라고 type을 추론함
const b2 = {hello:'world'}
//객체를 인터페이스로 타이핑할 수 있음 (인터페이스는 객체!)
interface Inter {
hello:string;
world?:number; // ?를 붙이면 world가 있어도 그만 없어도 그만인 속성
}
const b3:Inter = {hello:'interface'}; //world에 ?가 있어서 b3에 world를 안넣어도 에러가 안남
const b4:Inter = {hello:'interface', world:123};
type Type = {
hello:string;
func?:(param?:boolean) => void; //함수는 이런식으로 타이핑 가능
}
const c :Type = {hello:'type'}
조금만 코드가 복잡해지면 typescript의 type 추론을 실수 할 수 있음
너무 믿지는 말자
모듈 import
import fs from 'fs';
fs.readFile('package.json');
타입스크립트에서 위와 같이 모듈을 import 하면 아래의 에러가 발생
fs모듈의 타입 정의를 찾을 수 없다는 의미인데,
타입스크립트에서 모듈을 import 하려면 types/node 모듈명 을 따로 설치해야 사용할 수 있음
@types/node 패키지를 설치하면 된다.
npm i -D @types/node
그러면 @type 폴더가 생성되며 node에 대한 type 정의가 설치됨
npm에 많은 모듈, 라이브러리는 백만개가 넘음
많이 타입스크립트화 되어있음, 이 내용들을 보면 코드를 어떻게 사용해야하는지 설명서 처럼 알 수 있음
예를들자면, 파일시스템 모듈에 대해서 살펴보면
아래 빨간줄에서 F12를 누르면 (왼쪽 그림), 오른쪽 그림과 같이 readFile 메소드에 대한 type이 명시된 코드가 나옴
이 코드 말고 readFile 메소드가 여러개 정의되어 있는데 이는 overloading임
readFile 매개변수의 type을 보고 아래 처럼 오버로딩된 메소드를 사용하기 편리해졌음
import fs from 'fs';
//(path: PathOrFileDescriptor, callback: (err: NodeJS.ErrnoException | null, data: Buffer) => void,)
fs.readFile('package.json', ()=>{});
// export function readFile(
// path: PathOrFileDescriptor,
// options:
// | ({
// encoding?: null | undefined;
// flag?: string | undefined;
// } & Abortable)
// | undefined
// | null,
// callback: (err: NodeJS.ErrnoException | null, data: Buffer) => void,
// ): void;
fs.readFile('package.json',({encoding :null,flag:'sdf'}),()=>{});
이번에는 fs/promises 를 사용해보자
return에 Promise가 생김
그래서 readFile 매개변수에 콜백 함수를 지우고 then을 붙일 수 있음
참고
Promise도 reference를 들어가보면 (Promise에서 F12)

Promise<T>로 되어 있는데, T자리에는 어떤 타입이든 올 수 있는 제너릭 타입임
readFile에는 Promise<Buffer>로 되어 있으므로 T가 Buffer라고 해석하면 됨
TResult1 = T로 되어 있으므로, TResult1도 Buffer라고 해석하자
PromiseLike는 지금 그냥 Promise와 같다고 생각하면 됨
never는 사용할 수 없는 타입이므로 무시
따라서 제네릭이 포함된 코드는 최종적으로 다음과 같이 해석하면 됨

이렇듯 제너릭을 사용한 코드는 그때그때 해석이 달라짐
아래와 같이 코드를 수정하면 됨
import fs from 'fs/promises';
fs.readFile('package.json')
.then((result) => { //result Buffer 타입임
console.log(result);
})
.catch(console.error);
readFile의 반환값이 Promise 이므로 then과 catch를 붙일 수 있음
result 는 앞의 해석에 따라 Buffer 타입이 됨