
Today I Learn📖
- 모듈의 타입 선언 (강의)
- 타입 가져오기 / 내보내기 (강의)
- tsconfig.json 옵션 (강의)
- 내장 유틸리티 타입 (강의)
npm i @types/패키지명 -D을 통해 타입 정의 파일도 같이 설치node_modules - @types 안에 설치됨패키지명.d.ts 파일을 생성해 사용하려는 패키지에 대한 타입 정보를 따로 알려주기.d.ts파일: 타입 선언 전용 파일, 로직 없이 타입 정보만 정의함
-> declare를 사용해서 타입 선언
=> 패키지의 타입 선언 / 전역 타입 선언 용도
declare const version: string
declare function greet(name: string): void
// myPackage.d.ts -> 타입 정보가 없는 외부 모듈의 타입 선언
declare module "myPackage" {
export function doSomething(x: number): string
}
모듈 import 하려는데 기본 내보내기 방식이 아닐 경우
import * as로 가져올 수 있음tsconfig.json의 컴파일 옵션에 "esModuleInterop": true 옵션 사용 (보편적인 방법)import _ from '패키지명' // 에러 발생 -> 기본 내보내기가 아니기 때문
import * as _ from '패키지명' // 와일드 카드
// tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true
}
}
import _ from '패키지명' // 컴파일 옵션 추가시 사용 가능
lodash패키지
: 반복문, 배열 정리, 객체 다루기 등을 더 짧고 쉽게 할 수 있는 함수 라이브러리import _ from 'lodash'; // 패키지 설치 후 불러오기 ⠀ _.camelCase('I love Coffee~') // iLoveCoffee _.uniq([1, 2, 3, 2, 2, 4]); // [1, 2, 3, 4] (배열 중복 제거) _.cloneDeep(obj); // 객체 깊은 복사 _.isEmpty([]); // true (배열이 비었는지 확인) _.debounce(func, 300); // 디바운스 함수
해당 패키지가 타입 선언 파일이 존재하는지 확인하는 방법
1. 구글에패키지명 npm검색
2. 결과 중 npmjs.com 사이트 접속
3. 패키지명 옆에 버튼 확인
->DT있으면 자바스크립트로 만들어졌지만 타입 선언 파일도 존재하는 것
->TS있으면 애초에 타입스크립트로 만들어진 것 (타입 에러 발생 X)
allowJs: true옵션 추가: TS 프로젝트 안에서 JS 파일도 인식하도록 설정
-> JS에는 타입 정보가 없기 때문에 기본적으로 any로 추론됨
=> .d.ts 파일을 통해 타입 보강 필요
// tsconfig.json
{
"compilerOptions": {
"allowJs": true
}
}
d.ts파일 생성 방법
JS 모듈과 같은 파일에 JS모듈파일명.d.ts 파일 생성
types 파일 생성 -> 그 안에 d.ts 파일 생성
-> tsconfig.json에 "paths": [] 옵션으로 경로 설정
-> 그 이름을 전역적으로 사용 (상세 위치 없이)
// tsconfig.json
{
"compilerOptions": {
"paths": {
"myUtils": ["./js/myUtils.js"] // myUtils라는 이름으로 []에 들어있는 위치에서 파일을 참조하겠다는 뜻
}
}
}
// import 할 때
import utils, { add, subtract } from 'myUtils' // myUtils라는 경로별칭으로 그냥 사용
// myUtils.d.ts
declare module 'myUtils' { // 모듈 불러오듯 declare module 사용
// 내용 작성
interface Add {
(a: number, b: number): number
}
export const add: Add
}
export 붙임// myUtils.ts
export interface Add { // 타입 내보내기
(a: number, b: number): number
}
export interface Subtract { // 타입 내보내기
(a: number, b: number): number
}
export const add: Add = (a, b) => a + b // 값 내보내기
export const subtract = (a: number, b: number) => a - b // : Subtract를 풀어쓴 것, add: Add처럼 타입으로 갖다써도 됨
//main.ts
import { add, subtract } from './myUtils' // 데이터 가져오기
import type { Add, Subtract } from './myUtils' // 타입 가져오기 -> 직관적으로 타입임을 명시
// import { add, subtract, Add, Subtract } from './myUtils' 처럼 하나로 써도 되긴 함
const a = add(4, 7)
const b = subtract(9, 6)
const newAdd: Add = (x, y) => x + y // 가져온 타입 이용해서 데이터 생성
newAdd(1, 2)
const newSubtract: Subtract = (x, y) => x - y
newSubtract(1, 2)
import할 때
.ts 확장자를 명시적으로 안 붙이는 이유
: ES 모듈 방식과 호환되게 하기 위해 확장자를 생략
-> 명시적으로 확장자를 지정해버리면 파일을 탐색할 때 그 확장자만 찾게 되기 때문 (지정 안 하면 여러 가지를 시도함)
namespace: 여러 타입, 함수 등을 이름으로 묶어 그룹화하는 문법// myUtils.ts
export namespace MyUtils { // MyUtils라는 네임스페이스 지정 후 내보내기
export interface Result { // 네임스페이스 안에서 내보내기 할 기능은 또 export 붙여야함
value: number
}
export function add(a: number, b: number): number {
return a + b
}
export interface Subtract {
(a: number, b: number): number
}
}
export const subtract: MyUtils.Subtract = (a, b) => a + b // Subtract 타입이 MyUtils 안에 있으니까 앞에 써야함
// main.ts
import { subtract } from './myUtils' // 데이터 가져오기
import type { MyUtils } from './myUtils' // 타입 가져오기
const newSubtract: MyUtils.Subtract = (x, y) => x - y // 네임스페이스를 앞에 써줘야함
newSubtract(1, 2) // → -1
{
"compilerOptions": {
"strict": true, // 엄격한 타입 검사, 기본 false, TS는 true로 써야함
// 아래 옵션들이 전부 적용됨
// noImplicitAny: 암시적 any 금지
// noImplicitThis: 암시적 this 금지
// strictNullChecks: 엄격한 null, undefined 체크
// strictFunctionTypes: 엄격한 함수 매개변수 타입 검사 검사
// strictPropertyInitialization: 엄격한 클래스 속성 초기화 검사
// strictBindCallApply: bind, call, apply 메서드 인수 검사
"target": "ES2015", // 컴파일될 ES(JS) 버전
// 권장: `ES2015`
// 선택: `ES5`, `ES6` / `ES2015`, `ES2016`, `ESNext`, ...
"lib": ["ESNext", "DOM", "DOM.Iterable"], // 컴파일에서 사용할 라이브러리
// `target` 옵션에 따라 자동으로 지정됨
// 추천: `["ESNext", "DOM", "DOM.Iterable"]`
"module": "ESNext", // 사용할 모듈 방식
// 선택: `CommonJS`, `ES6` / `ES2015`, `ES2020`, `ESNext`
// `ES2020`: `import()`, `import.meta.url` 등 사용 가능
// `ESNext`: 가장 최신의 모듈 방식 사용
"moduleResolution": "bundler", // 컴파일러가 사용할 모듈 해석 방식
// 추천: `node` / `bundler` (최신은 bundler, 이전은 node 많이 사용)
// 선택: `classic`, `node`, `nodenext`, `bundler` 등
// `nodenext`: Node.js의 ES 모듈 지원
// `bundler`: Vite, esbuild, Webpack, Parcel 등 최신 번들러를 사용하는 경우 (>=5.0)
"paths": { // 경로 별칭 지정
"@utils/*": ["src/utils/*"] // 별칭: [위치]
}
"jsx": "react-jsx", // JSX 처리 방식
// `react-jsx`: JSX 변경, `.js` 파일로 출력 (`_jsx`), React@17 이상
// `react-jsxdev`: JSX 변경, `.js` 파일로 출력 (`_jsxDEV`), React@17 이상
// `react`: JSX 변경, `.js` 파일로 출력 (`React.createElement`)
// `react-native`: JSX 변경 없이, `.js` 파일로 출력
// `preserve`: JSX 변경 없이, `.jsx` 파일로 출력
"outDir": "dist", // 컴파일된 JS 파일 저장 위치
},
"files": ["./files/index.ts"], // 우선순위 1. 꼭 포함할 파일
"excludes": ["./node_modules", "./dist"], // 우선순위 2. 제외할 파일 및 폴더
"include": ["./src/**/*.ts"], // 우선순위 3. 포함할 파일 및 폴더
"extends": ["./config/tsconfig.base.json"] // 우선순위 4. 확장할 compilerOptions (같은 내용 있으면 기존 compilerOptions이 우선됨)
}
개발의존성 vs 일반의존성
- 개발의존성 (
-D) : 개발자가 코드 작성할 때 필요한 기능 ex) eslint, webpack, vite, ...
-> 코드 작성 / 빌드 / 테스트에만 필요하므로 배포시 포함 X- 일반의존성: 사용자가 서비스 사용할 때 필요한 기능 ex) react, axios, lodash, ...
-> 코드 실행, 서비스 동작에 필요하기 때문에 배포시 포함됨
타입스크립트가 가지고 있는 타입 변형 / 재구성 도구
-> 여러 유틸리티 타입을 조합해서 사용 가능
Partial<T>: 모든 속성을 선택적(Optional)으로 바꿔줌
type A = { name: string; age: number }
type B = Partial<A> // { name?: string; age?: number }
Required<T>: 모든 속성을 필수(Required)로 바꿔줌
type A = { name?: string; age?: number }
type B = Required<A> // { name: string; age: number }
Readonly<T>: 모든 속성을 읽기 전용(Readonly)으로 바꿔줌
interface User { name: string }
const A: User = { name: "A" }
const A: Readonly<User> = { name: "B" }
B.name = "BB" // 에러 발생 -> 읽기 전용이니까 수정 안 됨
Record<K, T>: K에 해당하는 key들이 T 타입의 속성들을 가지는 객체 타입 생성
type A = Record<'a' | 'b', number> // { a: number; b: number }
Pick<T, K>: 타입 T에서 K에 해당하는 속성만 골라서 새로운 타입 생성
type A = { name: string; age: number; email: string }
type B = Pick<A, 'name' | 'email'> // { name: string; email: string }
Omit<T, K>: 타입 T에서 K에 해당하는 속성을 제외한 새로운 타입 생성
type A = { name: string; age: number; email: string }
type B = Omit<A, 'age'> // { name: string; email: string }
Exclude<T, U>: T에서 U에 해당하는 타입을 제외한 타입 반환
type T = string | number | boolean
const a: Exclude<T, number | boolean> = 1234 // 에러 발생 -> 남은 T는 string이라서 타입 불일치
type A = Exclude<'a' | 'b' | 'c', 'b'> // 'a' | 'c', 직접 사용도 가능
Extract<T, U>: T에서 U에 공통되는 타입만 추출
type T = string | number | boolean
type T = number | boolean | string[]
const a: Extract<T, U> = "스트링입니다" // 에러 발생 -> 공통 타입은 number, boolean뿐
type A = Extract<'a' | 'b' | 'c', 'a' | 'c' | 'd'> // 'a' | 'c'
ReturnType<T>: 함수 타입 T의 반환 타입을 추출
function hello(msg: string) {
return `Hello ${msg}`
}
const a: ReturnType<typeof hello> = 'Only string'
const b: ReturnType<typeof hello> = 1234 // 에러 발생 -> hello의 반환 타입은 string임
Awaited<T>: Promise의 결과값 타입을 추출 (중첩된 Promise<Promise<T>>도 T로 한번에 추출 가능)
type A = Awaited<Promise<string>> // string
type B = Awaited<Promise<Promise<number>>> // number
이번 강의에서는 이론적인 것보다 실제 개발을 하면서 상식으로 알아두어야하는 내용들을 익힐 수 있었다.
외부 패키지의 타입 설치, tsconfig.json의 설정법, 타입 import / export 등 당연히 알고 있을 거라고 생각하지만 제대로 배운 적은 없는 정보들을 깊게 살펴볼 수 있어서 가려운 곳을 제대로 긁은 기분 !!
내장 유틸리티 타입들도 중요한 것들을 싹 훑어봐서 앞으로 계속 도움이 될 것 같다 ㅎㅎ