뉴히의 개발 로그
[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를 컴파일 한다
- .ts는 TypeScript 파일의 확장자
- 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
- 엄격한 타입 검사 옵션을 모두 활성화하는 옵션 / TypeScript 컴파일러가 보다 엄격한 타입 검사를 수행해 코드의 실수를 미리 찾아낼 수 있다. strict 옵션을 true로 설정하면 아래 옵션이 자동 true 설정됨
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 출력됨