«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
Recent Posts
Recent Comments
관리 메뉴

뉴히의 개발 로그

[TIL] 20230725 - TypeScript의 특징,장점/TypeScript 컴파일러 tsc/tsc 주요 명령어 / compilerOptions/@types 라이브러리/TypeScript 타입 (tuple/enum/readonly/any/unknown/union) 본문

개발일지/TIL

[TIL] 20230725 - TypeScript의 특징,장점/TypeScript 컴파일러 tsc/tsc 주요 명령어 / compilerOptions/@types 라이브러리/TypeScript 타입 (tuple/enum/readonly/any/unknown/union)

뉴히 2023. 7. 26. 00:03

JavaScript의 약점

  • JavaScript는 원래 웹 페이지에 동적인 효과를 주기 위해 만들어진 스크립트 언어이다. 동적 타입 언어는 예기치 않은 버그가 나올 확률이 매우 높다.
  • JavaScript는 변수의 타입이 실행시에 결정된다. 그렇기 때문에 변수에 잘못된 타입의 값이 할당되어 발생한 오류를 찾기 위해서는 실행 시간에 변수의 값과 타입을 모두 확인해야하는 번거로움이있다.
  • JavaScript는 let, const 같이 변수/상수를 구분하는 정도의 키워드만 지원된다.
  • JavaScript에서는 프로그래머가 마음만 먹으면 객체의 성질을 수시로 변화시킬 수 있어 자칫하면 실수로 엉뚱한 값이 나오거나 큰 오류가 날 수 있다.

그래서 TypeScript를 쓰면 좋다!!

TypeScript의 특/장점

  • TypeScript는 JavaScript의 모든 기능을 포함하고 추가적인 기능을 제공한다
  • TypeScript는 Microsoft에서 개발한 오픈 소스 프로그래밍 언어
  • 실행 시간에 결정되는 변수 타입 → 이제는 컴파일 시간에 변수의 타입을 체크
  • TypeScript 코드를 도입하고 나니 VS Code에서 코드를 입력하는 순간 알아서 오류 캐치
  • 편집기에서 코드 편집을 하지 않아도 실제로 실행을 시키면 컴파일 시간에 변수의 타입을 체크하기 때문에 실행이 되지 못하고 컴파일 에러를 발생시켜서 쓸데없는 에러 걱정을 하지 않아도 된다
// JavaScript 객체 지향 프로그래밍 예시

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  growOlder() {
    this.age += 1;
  }
}

const spartan = new Person('Spartan', 30);
spartan.age = 25; // 외부에서 age 속성을 마음대로 조작할 수 있어요! 뜻밖에 회춘?
spartan.growOlder();
console.log(spartan.age); // 결국 1살을 더 먹었지만 르탄이는 26세



// TypeScript 객체 지향 프로그래밍 예시

class Person {
  private name: string;
  private age: number; // age가 private인 것을 주목하세요!

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  public growOlder(): void {
    this.age += 1;
  }
}

const spartan = new Person('Spartan', 30);
spartan.age = 25; // Error: Property 'age' is private and only accessible within class 'Person'.
spartan.growOlder();
console.log(spartan.age); // Error: Property 'age' is private and only accessible within class 'Person'.

나이를 조회하게 하고 싶다면 getter 함수를 따로 작성하면 된다.

 

typeScript를 쓰면 ? 

  • 향상되는 생산성
  • 높아지는 안전성
  • 정적 타입 시스템
  • 객체 지향 프로그래밍
  • JavaScript에 비해 언어에서 보장되는 안정성을 바탕으로 테스트 코드가 줄어든다

 

 

컴파일러란?

  • 프로그래밍 언어로 작성된 소스 코드 → 다른 프로그래밍 언어로 변환
  • C언어 컴파일러는 C언어 → 기계어 코드 변환
  • TypeScript 컴파일러는 소스 코드의 정적 타입 검사를 수행
  • 컴파일러는 변환과정에서 소스 코드에서 문제가 발견되면 에러 메시지를 출력해주기때문에  -> 문제 신속해결 가능!
  • 최적화! 코드가 최적화되면 전반적인 어플리케이션 실행 시간이 더 빨라진다. 이걸 자동으로 도와주는게 컴파일러

 

TypeScript의 타입을 검사해주는 컴파일러 tsc

  • 타입스크립트 컴파일러인 tsc는 TypeScript → JavaScript 코드 변환해준다
  • JavaScript는 동적 언어(=인터프리터 언어)이기 때문에 기계어로 변환될 필요가 없다
    • Node.js나 Chrome → JavaScript를 실행할 때는 V8 엔진이 코드 해석 및 실행
    • Firefox → JavaScript를 실행할 때는 SpiderMonkey가 코드 해석 및 실행
정적 언어(=컴파일 언어) → 기계어로 변환이 되어야 한다.
동적 언어(=인터프리터 언어) → 엔진이 코드를 한 줄씩 실행하면서 동적으로 해석

 

 

 tsc 주요 명령어

  • tsc —-init
    • tsconfig.json이 생성되는 명령어
  • tsc index.ts
    • index.ts를 컴파일 한다
    • .tsTypeScript 파일의 확장자
  • tsc src/*.ts
    • src 디렉토리 안에 있는 모든 TypeScript 파일을 컴파일 한다.
  • tsc index.js --declaration --emitDeclarationOnly
    • @types 패키지를 위한**.d.ts 파일 생성**을 하는 명령
    • TypeScript로 작성된 모듈이 아니라 JavaScript로 작성된 모듈에 타입 선언을 제공할 때 유용하게 쓰인다.
  • 옵션 매뉴얼 https://www.typescriptlang.org/ko/tsconfig

 

compilerOptions - target 옵션

어떤 JavaScript 버전으로 변환을 할 지 정하는 옵션

  • es5 로 설정하면 CommonJS 버전으로 컴파일
  • es2016(=es7) 로 설정하면 ES2016 버전으로 컴파일
    • 최신 브라우저는 보통 ES2016을 지원하니 이렇게 설정하시는 것을 추천

compilerOptions - module 옵션

  • 모듈을 가져오고 내보내는 방식을 결정하는 옵션
  • TypeScript 파일을 컴파일한 후 생성되는 JavaScript 모듈의 형식을 지정
  • target 옵션과는 서로 독립적인 관계니 프로젝트의 요구사항에 따라 옵션을 설정하면 됨

compilerOptions - outDir 옵션

  • 컴파일된 JavaScript 파일이 저장될 출력 디렉터리를 지정
  • 예를 들어, "outDir": "dist"로 설정하면 컴파일된 파일들이 dist 폴더에 저장
  • compilerOptions - strict 옵션
    • 엄격한 타입 검사 옵션을 모두 활성화하는 옵션 / TypeScript 컴파일러가 보다 엄격한 타입 검사를 수행해 코드의 실수를 미리 찾아낼 수 있다. strict 옵션을 true로 설정하면 아래 옵션이 자동 true 설정됨
      • strictNullChecks : 잠재적으로 null(undefined)이 될 수 있는 값들에 대해서 엄격하게 확인하는 옵션
      • strictFunctionTypes
      • strictBindCallApply
      • strictPropertyInitialization
      • noImplicitAny
        • 함수의 인자 또는 변수의 타입이 명시적으로 선언되지 않은 경우에 컴파일러가 자동으로 any타입을 부여하지 않도록 하는 옵션 -> 이 옵션을 활성화하면 개발자가 누락된 타입 선언을 확인하고 명시적으로 타입을 선언할 수 있다
      • noImplicitThis
      • alwaysStrict

compilerOptions - sourceMap 옵션

  • 컴파일된 JavaScript 파일에 대한 소스 맵을 생성하는 옵션
  • 소스 맵을 사용하면 실행 중에 에러가 발생했을 때 원래 TypeScript 소스 코드의 위치를 확인할 수 있다
  • 코드 디버깅에 매우 큰 도움이 되기 때문에 개발 환경에서는 꼭 true로 설정하시는 것을 권장
    • 프로덕션 환경에서는 용량이나 성능상의 이유로 sourceMap을 사용하지 않는 것이 나을 수 있다

include , exclude 옵션

  • tsc가 컴파일을 할 때 포함하거나 제외할 파일이나 디렉터리를 지정하는 옵션
  • “include": ["src/**/*"]    : src 디렉토리 밑의 친구들을 컴파일 하겠다는 의미
  • "exclude": ["node_modules", "dist"]   : node_modules, dist 디렉토리 밑의 친구들은 컴파일 대상에서 제외하겠다는 의미

 

 

@types 라이브러리

TypeScript는 @types 라이브러리를 통해 외부 라이브러리에 대한 타입 정보를 제공

TypeScript에서 JavaScript 라이브러리에 대한 .d.ts 파일을 제공하면 JS 라이브러리를 그대로 쓸수 있다

 

TypeScript 기본 타입

  • boolean : 참(true) 또는 거짓(false)
  • number : 정수, 실수 뿐 아니라 2, 8, 16진수까지 표현할 수 있다
  • string : 작은 따옴표(’), 큰 따옴표(”), 백쿼트(`) 를 사용하여 문자열을 표현
  • array : 기본타입에 []가 붙은 형태의 타입
    • ex)  numbers : number[] / strings :  string[]
    • const testScores: number[] = [90, 85, 78, 92];
  • 튜플(tuple) : 서로 다른 타입의 원소를 순서에 맞게 가질 수 있는 특수한 형태의 배열
    • ex) const person: [string, number, boolean] = ['Spartan', 25, false];
    • 같은 타입의 개수와 순서를 맞춰 써야한다.
  • enum : 열거형 데이터 타입
    • 다양한 상수를 보다 더 이해하기 쉬운 문자열 이름으로 접근하고 사용할 수 있게 하는 타입
    • enum 안에 있는 각 요소는 값이 설정되어 있지 않으면 기본적으로 숫자 0으로 시작
    • enum 안에 있는 요소에는 number 혹은 string타입의 값만을 할당할 수 있다
// enum 사용 예시

enum UserRole {
  ADMIN = "ADMIN",
  EDITOR = "EDITOR",
  USER = "USER",
}

enum UserLevel {
  NOT_OPERATOR, // 0
  OPERATOR // 1
}

function checkPermission(userRole: UserRole, userLevel: UserLevel): void {
  if (userLevel === UserLevel.NOT_OPERATOR) {
    console.log('당신은 일반 사용자 레벨이에요');
  } else {
    console.log('당신은 운영자 레벨이군요');
  } 

  if (userRole === UserRole.ADMIN) {
    console.log("당신은 어드민이군요");
  } else if (userRole === UserRole.EDITOR) {
    console.log("당신은 에디터에요");
  } else {
    console.log("당신은 사용자군요");
  }
}

const userRole: UserRole = UserRole.EDITOR;
const userLevel: UserLevel = UserLevel.NOT_OPERATOR;
checkPermission(userRole, userLevel);

 

  • let :  변경 가능한 변수값을 할당하는 변수 선언
  • const : 값을 변경 할 수 없는 상수 선언
  • readonly :  readonly는 TypeScript에서 객체의 속성을 불변으로 만드는 데 사용되는 키워드, 클래스의 속성이나 인터페이스의 속성을 변경할 수 없게만들 수있다.
// readonly 사용 예시

class Person { // 클래스는 다른 강의에서 자세히 설명해드릴게요!
  readonly name: string;
  readonly age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

const person = new Person('Spartan', 30);

console.log(person.name);  // 출력: 'Spartan'
console.log(person.age);   // 출력: 30

person.name = 'Jane';  // 에러: 'name'은 readonly 속성이므로 다시 할당할 수 없어요!
person.age = 25;       // 에러: 'age'은 readonly 속성이므로 다시 할당할 수 없어요!

 

  • any 타입 : 모든 타입의 슈퍼 타입 - 어떤 타입의 값이든 저장할 수 있다 but, 코드의 안정성과 유지 보수성을 저해할 수 있어 가급적 사용을 지양해야한다. (== js object type과 같다)
let anything: any;
anything = 5; // 최초에는 숫자를 넣었지만
anything = 'Hello'; // 문자열도 들어가고요
anything = { id: 1, name: 'John' }; // JSON도 들어가네요

 

  • unknown 타입 : unknown 타입은 any 타입과 비슷한 역할을 하지만 더 안전한 방식으로 동작 -> 다른 타입의 변수에 할당하려면 명시적으로 타입을 확인해야 한다.
// Type Assertion (타입단언)  

let unknownValue: unknown = '나는 문자열이지롱!';
console.log(unknownValue); // 나는 문자열이지롱!

let stringValue: string;
stringValue = unknownValue; // 에러 발생! unknownValue가 string임이 보장이 안되기 때문!
stringValue = unknownValue as string;  //  <--- Type Assertion(타입 단언)
console.log(stringValue); // 나는 문자열이지롱!



// typeof를 이용해 타입체크를 미리한 후 unknown 타입의 변수를 string 타입의 변수에 할당할 수 있다 

let unknownValue: unknown = '나는 문자열이지롱!';
let stringValue: string;

if (typeof unknownValue === 'string') {
  stringValue = unknownValue;
  console.log('unknownValue는 문자열이네요~');
} else {
  console.log('unknownValue는 문자열이 아니었습니다~');
}





===> Type Assertion (타입단언) 을 사용하는게 더 좋당

 

  • union : 여러 타입 중 하나를 가질 수 있는 변수를 선언할 때 사용
    • union은 | 연산자(파이프연산자)를 사용하여 여러 타입을 결합하여 표현 
    • unknown 타입도 결국 재할당이 일어나지 않으면 타입 안전성이 보장되지 않는다  -> 이럴 때 union 타입 사용
type StringOrNumber = string | number; // 원한다면 | boolean 이런식으로 타입 추가가 가능해요!

function processValue(value: StringOrNumber) {
  if (typeof value === 'string') {
    // value는 여기서 string 타입으로 간주됩니다.
    console.log('String value:', value);
  } else if (typeof value === 'number') {
    // value는 여기서 number 타입으로 간주되구요!
    console.log('Number value:', value);
  }
}

processValue('Hello');
processValue(42);

 

TypeScript를 쓰면서 여러 타입을 하나의 변수로 해결하겠다는 생각은 가급적 지양해라!!

 


 

TypeScript 실습!

 

1) node.js 프로젝트 생성할 폴더 생성

2) package.json 생성

npm init -y

3) tsconfig.json 생성 -> TypeScript 프로젝트로 변환

tsc --init

4) tsconfig.json을 열어서 아래의 옵션을 주석 해제하여 true로 설정

"allowJs": true // TypeScript 프로젝트에 JavaScript 파일 허용 여부
"checkJs": true // JavaScript 파일 타입 체크 여부

5) TypeScript에서 사용하고 싶은 커스텀 JavaScript 라이브러리(test.js)를 생성

/** // JSDoc 형태의 주석 : 자바스크립트의 api를 문서화해주는 html 문서 편집기
 * @param {number} a
 * @param {number} b
 * @returns {number}
 */ // 최소한의 정보로 타입을 명시해줌
 
export function add(a, b) { // export를 넣지 않으면 import 할 수 없는 것 아시죠?
  return a + b;
}

* JSDoc으로 자바스크립트 소스코드에 타입 힌트를 제공할 수 있다!

 

6) JSDoc으로 타입 힌트가 제공된 test.js의 .d.ts 파일을 만듭니다. 다음의 명령어를 복사 + 붙여넣기를 해서 터미널에서 실행

npx tsc test.js --declaration --allowJs --emitDeclarationOnly --outDir types

7) types/test.d.ts 파일을 확인하면 다음과 같이 생성

/**
 * @param {number} a
 * @param {number} b
 * @returns {number}
 */
export function add(a: number, b: number): number;

8) test.js 파일을 참조할 foo.ts 파일을 생성

import { add } from "./test";
console.log(add(1, 2));

9) foo.ts 파일 터미널에서 실행

npx ts-node foo.ts

// 3 출력됨