TIL | TypeScript (컴파일러, tsc ... )

·2023년 7월 27일

TIL # WIL

목록 보기
37/65

23.07.27

2-1 ~ 2-3 컴파일러, tsc

TS 컴파일러인 tsc는 JS로 변환함

원래 컴파일러는 기계어로 변환되어야 하는데 JS는 동적 언어(= 인터프리터 언어)이기 때문에 기계어로 변환될 필요가 없음
그래서 Node.js나 크롬에서 JS를 실행할 때는 v8엔진이 코드 해석 및 실행을 해서

compilerOptions - strict
compilerOptions - sourceMap

위 두 옵션 모두 true로 설정하는 것을 권장

tsc —init을 실행하면 tsconfig.json이 생성됨

그렇다면 여기서 tsconfig.json란 ? TS 프로젝트의 설정 파일로 프로젝트의 컴파일 옵션 및 입력 파일들을 정의하는데 사용

tsconfig.json 주요 옵션
매뉴얼 https://www.typescriptlang.org/ko/tsconfig

옵션 종류
1. compilerOptions - target 옵션
ES6 이전 버전들은 Common JS라고 불림
그 이후 버전들은 그냥 ES6, ES7 or ES2015, ES2016 으로 불림

옵션을 정할 때는 TS가 어느 환경에서 실행이 되어야하는지를 고려해야 함

  • 만약, 내가 만든 프로젝트가 생각보다 레거시한 환경에서 동작해야 된다면? → es5
  • 그렇지 않다면 → es2016
  1. compilerOptions - module 옵션
  • TS 파일을 컴파일한 후 생성되는 JS 모듈의 형식을 지정
  • 모듈을 가져오고 내보내는 방식을 결정하는 옵션
  • target 옵션과는 서로 독립적인 관계니 프로젝트의 요구사항에 따라 옵션을 설정하면 됨
  1.  compilerOptions - outDir 옵션
  • 컴파일된 JavaScript 파일이 저장될 출력 디렉터리를 지정
  • ex. "outDir": "dist"로 설정하면 컴파일된 파일들이 dist 폴더에 저장
  1. compilerOptions - sourceMap 옵션
  • 컴파일된 JavaScript 파일에 대한 소스 맵을 생성하는 옵션
  • 소스 맵을 사용하면 실행 중에 에러가 발생했을 때 원래 TypeScript 소스 코드의 위치를 확인할 수 있음
  • 코드 디버깅에 매우 큰 도움이 되기 때문에 개발 환경에서는 꼭 true로 설정하시는 것을 권장
  1. include , exclude 옵션
  • tsc가 컴파일을 할 때 포함하거나 제외할 파일이나 디렉터리를 지정하는 옵션
  • “include": ["src/*/"]
    • src 디렉토리 밑의 친구들을 컴파일 하겠다는 의미
  • "exclude": ["node_modules", "dist"]
    • node_modules, dist 디렉토리 밑의 친구들은 컴파일 대상에서 제외하겠다는 의미

2-4 ~ 2-6 .d.ts 파일

레거시한 JS 파일을 TS로 사용하기 위해서는 .d.ts 파일을 이용하여 하위 호환성을 확보하면 됨

그렇다면 .d.ts 파일이란 ? TypeScript 타입 정의 파일 => 즉, JavaScript 라이브러리에 대한 타입 정보를 제공

.d.ts 파일로 tsc는 (1) 외부 라이브러리의 함수 타입 정보 (2) 외부 라이브러리 클래스 타입 정보 (3) 외부 라이브러리 객체 타입 정보 뿐만 아니라, (4) .d.ts 파일로 외부 라이브러리의 타입 추론 까지 가능하며
이 파일을 제공하면 TS에서도 JS라이브러리를 코드 한줄도 수정할 필요없이 그대로 사용할 수 있음 ⭐️
이게 안된다면 호환성이 안좋아 인기가 없었을듯

npm init -y
=> Node.js 프로젝트 만드는 명령어

tsc --init
=> TS 프로젝트 만드는 명령어

"allowJs": TS 프로젝트에서 JS를 허락할건지 여부
"checkJs": JS 파일 타입 체크할건지 여부

/**

  • @param {number} a
  • @param {number} b
  • @returns {number}
    */
    export function add(a, b) { // export를 넣지 않으면 import 할 수 없는 것 아시죠?
    return a + b;
    }
  1. 위의 주석문은 JSDoc이라고 해요!
  2. JSDoc은 API의 시그니처 (인자, 리턴 타입)를 설명하는 HTML 문서 생성기에요!
  3. JSDoc으로 자바스크립트 소스코드에 타입 힌트를 제공할 수 있어요!

tsc --init --rootDir ./src --outDir ./dist --esModuleInterop --module commonjs --strict true --allowJS true --checkJS true

  • --rootDir ./src
    • 프로그램의 소스 파일이 들어가는 경로는 src 디렉토리입니다.
  • --outDir ./dist
    • 컴파일이 된 파일들이 들어가는 디렉토리는 dist 디렉토리입니다.
  • --esModuleInterop
    • CommonJS 방식의 모듈을 ES모듈 방식의 import 구문으로 가져올 수 있습니다!

function calculateAverage(student: Student): number {
const sum =
student.scores.korean +
student.scores.math +
student.scores.society +
student.scores.science +
student.scores.english;

// const average = sum / 5;
// 하드코딩하지 않게
const average = sum / Object.keys(student.scores).length;
return average;
}

Object.keys 라는

3-1. 타입을 왜 제대로 알아야 하는가

변수는 데이터를 저장하는 공간

변수에는 다양한 데이터 타입이 저장될 수 있음

  • 잘못된 타입을 사용할 경우 => 오류가 일어나는 가장 빈번한 사례
  • 타입 안정성 = 코드가 예상한 대로 동작함을 보장하는 것
    타입을 제대로 이해하고 활용한다면 타입 안정성이 높을 것이고 자연스럽게 테스트, 디버깅 시간도 줄일 수 있음

  • 원활한 협업 가능

3-2 ~ 3-3. 기본 타입
1. boolean : true, false => 2가지의 상태를 표현하고 싶은 경우로 조건문, 비교 연산에서 주로 사용
3가지 이상의 상태를 표현하고 싶은 경우는 enum, string …
2. number : 숫자
3. string : 문자 (* 템플릿 리터럴을 사용할 땐 ``사용)
4. 배열 : 기본타입에 []가 붙은 형태의 타입
5. 튜플 (tuple) : 서로 다른 타입의 원소를 순서에 맞게 가질 수 있는 특수한 형태의 배열

배열은 같은 타입의 원소 데이터만 가질 수 있지만 튜플은 상관없음

const person: [string, number, boolean] = ['Spartan', 25, false];
const person2: [string, number, boolean] = [25, 'Spartan', false]; // 오류!

이런식으로 어떤 타입의 원소를 허용할 것인지 정의해주고 할당해주면 됨
그러나 정의된 데이터 타입의 개수와 순서에 맞추어 할당하는 것이 필수
또한 push를 사용하여 개수를 더 추가할 순 있지만 구조가 내부적으로 변경되니 좋은 방법은 아님

  1. enum : 열거형 데이터 타입 => 다양한 상수를 보다 더 이해하기 쉬운 문자열 이름으로 접근하고 사용할 수 있게 하는 타입
  • enum 안에 있는 각 요소는 값이 설정되어 있지 않으면 기본적으로 숫자 0, 있으면 1
  • enum 안에 있는 요소에는 number 혹은 string타입의 값만을 할당가능

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

enum UserLevel {
NOT_OPERATOR, // 0 // 값을 대입하지 않으면 0
OPERATOR // 1 // 대입하면 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);

이렇듯 enum은 명확하게 관련된 상수값들을 그룹화하고자 할 때 사용하는게 좋음
그러나 값의 수가 많지 않고, 값들 사이의 관계가 뚜렷하지 않으면 사용하지 않는게 좋음 (=> 즉 카테고라이징 가능한 값들에서만 사용하기)

3-4 const, readonly

let : 변수, 값 변경 가능, 재할당 불가

const : 상수, 값 변경 불가 ( “= 연산자”로 다시 할당이 불가능하다는 의미), 재할당 불가

const num: number = 5;
console.log(num); // 출력: 5

num = 10; // 에러: 'num'은 const로 선언되었으므로 다시 할당될 수 없어요!

const nums: number[] = [];
console.log(nums); // 출력: []
nums.push(1); // 할당은 되지 않아도 배열에 데이터를 추가/삭제하는 것은 문제가 안됩니다!
nums.push(2); // 은근히 헷갈릴 수 있지만 = 연산자 기준으로만 생각하면 매우 쉬워요!
console.log(nums); // 출력: [1, 2]

nums = []; // 에러: 'nums'는 const로 선언되었으므로 다시 할당될 수 없어요!

“= 연산자”를 사용하여 재할당은 불가하지만 배열 안에서 메서드를 사용하여 데이터 추가/삭제는 가능

========================

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 속성이므로 다시 할당할 수 없어요!

readonly를 const로 치환하면 어떻게 되나요? 이렇게 쓰면 안되나요?

  • 클래스의 속성에 const 키워드를 사용할 수 없다고 편집기가 에러를 뿜습니다!
  • const 키워드는 일반 변수를 상수화 할 때 사용하는 것이에요!

3-5 any, unknown, union

가변적인 타입의 데이터를 저장하고 싶다면 unknown
가변적인 타입을 일일이 정의할 수 있다면 union

  1. any : 모든 타입의 슈퍼 타입 => 즉, 어떤 타입의 값이든 저장할 수 있다는 의미 => JS에 object와 같은 최상위 타입 !!

let anything: any;
anything = 5; // 최초에는 숫자를 넣었지만
anything = 'Hello'; // 문자열도 들어가고요
anything = { id: 1, name: 'John' }; // JSON도 들어가네요

=> 이러한 경우가 정말 많이 없긴하지만 NoSQL 같은 데이터를 받아들일 때 사용할 수 있음

  • TypeScript를 사용하는 주된 이유 중 하나는 프로그램의 타입 안정성을 확보하기 위한 것
  • 그런데, any 타입은 그러한 우리의 믿음을 송두리째 저버릴 수 있는 아주 위험한 친구
  • any 타입은 코드의 안정성과 유지 보수성을 저해할 수 있어요. 가급적 사용금지
  1. unknown : 모든 타입의 값을 저장할 수 있으나, 이 값을 다른 타입의 변수에 할당하려면 명시적으로 타입을 확인

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

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

  • stringValue = unknownValue as string; 코드를 Type Assertion(타입 단언)이라고 합니다.
  • unkwown 타입의 변수를 다른 곳에서 사용하려면 타입 단언을 통해 타입 보장을 하여 사용할 수 있어요!

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

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

  • 타입 단언만이 답은 아닙니다!
  • typeof 키워드를 이용하여 타입 체크를 미리한 후 unknown 타입의 변수를 string 타입의 변수에 할당할 수 있어요!
  1. union
  • unknown 타입이 그나마 재할당을 할 때 타입 체크가 되어서 안전함을 보장해요.

  • 하지만, unknown 타입도 결국 재할당이 일어나지 않으면 타입 안전성이 보장이 되지 않음

  • union 은 여러 타입 중 하나를 가질 수 있는 변수를 선언할 때 사용됩니다!

  • 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를 쓰면서 여러 타입을 하나의 변수로 해결하겠다는 생각은 가급적 지양해주세요!
  • 이런 사소한 습관들이 여러분들의 코드의 안정성을 높이고 유지 보수성을 개선할 수 있다는 것을 명심해주세요!
    => 위와 같은 3가지는 예외적인 상황이니 웬만하면 사용 자제하면 좋음

0개의 댓글