Skip to content

[FE] TypeScript 컨벤션

최혜령 edited this page Nov 8, 2023 · 3 revisions

🧐 집사의고민팀의 생각

✅ Component

선언 방식

  • 함수 선언문 vs 표현식
    • 다른 함수 선언에도 표현식을 사용했기 때문에 코드의 일관성 유지를 위해 컴포넌트를 작성할 때에도 함수 표현식을 사용했습니다.

Props

  • type vs interface vs inline
  • 네이밍 방식: {컴포넌트명}Props

Component with Children / Component without Children

  • VFC, FC, PropsWithChildren

    • 일반적인 경우에는 PropsWithChildren을 사용하고 외부에선 타입 추론이 안 되도록 감추고 내부에서만 사용하고 싶은 prop이 있을 경우 React.FCPropsWithChildren을 사용합니다.
      • React.FC를 사용하면 Children타입을 명시할 필요 없음
      • React.FC<T> = T & {children?:ReactNode}
    type Private<T> = T & { __private__ : boolean }
    
    interface MyProps {}
    
    const Component:React.FC<MyProps> = (props:PropsWithChildren<Private<MyProps>>) => {
    	const {__private__} = props;
    }
    const obj = {__private__.: true}
    
    <Component __private__ = {}/>
  • Children, render메서드/컴포넌트 반환 타입

    • JSX.Element vs React.ReactElement vs ReactNode

      type ReactNode = ReactElement | string | number | ReactFragment | ReactPortal | boolean | null | undefined;

Event

  • Event Handler vs Event
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => { /*...*/ };
// vs
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { /*...*/ };
  • import 방식
    • React.MouseEventHandler vs MouseEventHandler

✅ Hooks

기본 훅

// useState 예시
// 타입과 초기값을 지정하는 방식 
const [data, setUser] = useState<User | undefined>(undefined);
const [data, setUser] = useState<User | null>(null); 
const [user, setUser] = useState<User>({});
const [user, setUser] = useState<boolean>(true);

의도한 대로 타입추론이 되는 경우 타입 생략 vs 데이터가 반환되는 훅의 경우 타입 지정하기

Ref

  • MutableRefObject vs RefObject
// ref의 용도에 맞게 오버로딩 된 타입을 활용한다

// current를 변경하고 싶은 경우
const ref = useRef() // MutableRefObject
const ref = useRef(3) // MutableRefObject
const ref = useRef<number>() // MutableRefObject ✅

// current를 변경하고 싶지 않은 경우
const ref = useRef<number | null>(null) // RefObject ✅

✅ 모듈

type 관리 방식

  • 네이밍 컨벤션
    • Props 타입 네이밍 → ${컴포넌트 이름}Props
  • 디렉토리 구조: 도메인 별 구분
📦types
  📂assets
   📜assets.d.ts
  📂common
   📜errorBoundary.ts
   📜utility.ts
  📂food
   📜client.ts
   📜remote.ts
  📂review
   📜client.ts
   📜remote.ts
  • 선언 위치: Props → 같은 컴포넌트 파일 내의 컴포넌트 위, 나머지 → types 디렉토리

type import/export

  • import/export 방식
    • 타입스크립트와 별개로도 모듈 import/export 규칙 논의해보기
  • Type-Only Import and Exports 사용 여부? 🙆‍♂️
    • 빌드 속도, 타입 안정성, 가독성 향상

import type { SomeThing } from "./some-module.js";
export type { SomeThing };

vs


import { SomeThing } from "./some-module.js";
export type SomeThing; 
// 예시
type Food = {
  id: number;
  name: string;
  imageUrl: string;
  purchaseUrl: string;
};

export type { Food };

✅ API

Request / Response Type

  • API 호출 로직에서 Request / Response 데이터를 다루는 방식
  • /src/types/${domain}/reomote.ts에서 관리
interface GetSomethingReq {

}

interface GetSomethingRes {

}
  • Request, Response 데이터의 타입을 나누어 Interface로 선언

✅ 빌드 설정

loader

  • TS 컴파일을 위해 어떤 loader를 사용하고 있는지와 선택 이유
    • 참고) babel-loader, ts-loader, esbuild-loader
    • @babel/preset-typescript : ts-loader 없이 typescript 트랜스파일링 가능
    • ForkTsCheckerWebpackPlugin: 빌드 과정에서 type 체크 가능(ts-loader 보다 뛰어난 속도)

tsconfig

  • 설정 기준과 설정한 항목들에 대한 이해
{
  "compilerOptions": {
    "target": "es6",                                
    "lib": ["dom", "dom.iterable", "ESNext"],        // 컴파일에 포함할 표준 라이브러리를 지정합니다 (DOM 관련 API들)
    "jsx": "react-jsx",                            
    "module": "ESNext",                             // 컴파일에 사용할 모듈 시스템을 지정합니다 (ECMAScript Modules)
    "moduleResolution": "node",                     // 모듈 해석 전략을 지정합니다 (Node.js 스타일)
    "baseUrl": ".",                                 
    "resolveJsonModule": true,                      // JSON 모듈을 가져오도록 허용합니다
    "allowJs": true,                                // JavaScript 파일도 컴파일하도록 허용합니다
    "declaration": true,                           // .d.ts 형태의 선언 파일을 생성합니다
    "outDir": "./lib",                             // 생성된 .d.ts 선언 파일의 출력 디렉토리를 지정합니다
    "noEmitOnError": true,                         // 오류 발생 시 TypeScript 출력물을 생성하지 않도록 설정합니다
    "isolatedModules": true,                      // 다른 타입의 import를 같은 모듈에서 허용하지 않습니다
    "allowSyntheticDefaultImports": true,         // 'import x from "module-name"' 형태의 모듈을 허용합니다
    "esModuleInterop": true,                      // ESMInterop을 위해 __importStar와 __importDefault 도우미를 생성합니다
    "forceConsistentCasingInFileNames": true,    // 파일 이름에서 일관된 대소문자를 강제합니다 (대소문자를 구분하는 경로를 적용합니다)
    "strict": true,                              // 엄격한 타입 체크 옵션을 활성화합니다
    "noImplicitAny": true,                      // 암시적으로 'any' 타입을 허용하지 않습니다
    "noFallthroughCasesInSwitch": true,        // switch문에서 모든 case에 'break' 또는 'return' 문이 없을 경우 오류를 발생시킵니다
    "skipLibCheck": true,                      // 선언 파일 (*.d.ts)의 타입 체크를 건너뜁니다
    "paths": {                                 // 모듈 이름에 대한 경로 매핑 별칭을 지정합니다 (예: "@/*"는 "src/*"를 가리킵니다)
      "@/*": ["src/*"]
    }
  },
  "include": ["src"],                        
  "exclude": ["dist", "./src/**/*.stories.tsx"]
}

🧙‍♂️ 공유하고 싶은 내용

뿌듯한 부분, 도움이 필요한 부분, 우리 팀의 팁 등등

🔗 참고한 자료

Clone this wiki locally