Same architecture as before, adapted for TypeScript conventions — .tsx/.ts extensions, a dedicated types/ system, strict config files, and typed everything.
my-app/
├── public/
│ ├── favicon.ico
│ ├── robots.txt
│ └── index.html
│
├── src/
│ ├── assets/
│ │ ├── images/
│ │ ├── fonts/
│ │ └── icons/
│ │
│ ├── components/
│ │ ├── common/
│ │ │ ├── Button/
│ │ │ │ ├── Button.tsx
│ │ │ │ ├── Button.module.css
│ │ │ │ ├── Button.types.ts
│ │ │ │ ├── Button.test.tsx
│ │ │ │ └── index.ts
│ │ │ └── Modal/
│ │ └── layout/
│ │ ├── Header/
│ │ └── Footer/
│ │
│ ├── features/ # Feature-based modules
│ │ ├── auth/
│ │ │ ├── components/
│ │ │ │ └── LoginForm.tsx
│ │ │ ├── hooks/
│ │ │ │ └── useAuth.ts
│ │ │ ├── services/
│ │ │ │ └── authService.ts
│ │ │ ├── types/
│ │ │ │ └── auth.types.ts
│ │ │ ├── authSlice.ts # Redux Toolkit slice / Zustand store
│ │ │ └── index.ts
│ │ ├── dashboard/
│ │ └── profile/
│ │
│ ├── pages/
│ │ ├── Home/
│ │ │ ├── Home.tsx
│ │ │ └── Home.module.css
│ │ ├── About/
│ │ └── NotFound/
│ │
│ ├── routes/
│ │ ├── AppRoutes.tsx
│ │ ├── ProtectedRoute.tsx
│ │ └── routes.types.ts
│ │
│ ├── hooks/ # Global/shared custom hooks
│ │ ├── useDebounce.ts
│ │ ├── useFetch.ts
│ │ └── useLocalStorage.ts
│ │
│ ├── context/
│ │ ├── ThemeContext.tsx
│ │ └── AuthContext.tsx
│ │
│ ├── store/ # Global state management
│ │ ├── index.ts
│ │ ├── hooks.ts # Typed useDispatch/useSelector
│ │ └── slices/
│ │ └── userSlice.ts
│ │
│ ├── services/ # API layer
│ │ ├── api.ts # Axios instance/config (typed)
│ │ ├── authService.ts
│ │ └── userService.ts
│ │
│ ├── utils/
│ │ ├── formatDate.ts
│ │ ├── validators.ts
│ │ └── constants.ts
│ │
│ ├── types/ # Global/shared TypeScript types
│ │ ├── index.ts # Re-exports
│ │ ├── user.types.ts
│ │ ├── api.types.ts
│ │ ├── env.d.ts # Typed process.env / import.meta.env
│ │ └── global.d.ts # Ambient/global declarations
│ │
│ ├── styles/
│ │ ├── globals.css
│ │ ├── variables.css
│ │ └── theme.ts # Typed theme object (MUI/styled-components)
│ │
│ ├── config/
│ │ ├── env.ts # Typed env config
│ │ └── appConfig.ts
│ │
│ ├── App.tsx
│ ├── App.css
│ ├── main.tsx # Entry point (Vite) / index.tsx (CRA)
│ └── vite-env.d.ts # Vite's built-in type reference
│
├── tests/
│
├── .env
├── .env.example
├── .eslintrc.cjs
├── .prettierrc
├── .gitignore
├── tsconfig.json # TS compiler config
├── tsconfig.node.json # For vite.config.ts itself
├── package.json
├── vite.config.ts
└── README.md
What Changes vs. the JS Version
1. Every file gets typed extensions
.jsx → .tsx (files with JSX), .js → .ts (pure logic files like utils, services, hooks without JSX).
2. Dedicated types/ folders, at two levels
Global types (src/types/) — shared across the app: API response shapes, User model, env typings.
Feature-local types (features/auth/types/) — scoped to that domain only.
3. tsconfig.json is critical
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"]
}
4. Typed component pattern
// Button.types.ts
export interface ButtonProps {
label: string;
variant?: 'primary' | 'secondary';
onClick?: () => void;
disabled?: boolean;
}
// Button.tsx
import { FC } from 'react';
import { ButtonProps } from './Button.types';
const Button: FC<ButtonProps> = ({ label, variant = 'primary', onClick, disabled }) => {
return (
<button className={`btn btn-${variant}`} onClick={onClick} disabled={disabled}>
{label}
</button>
);
};
export default Button;
5. Typed API service layer
// services/api.ts
import axios, { AxiosInstance } from 'axios';
const api: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000,
});
export default api;
// services/userService.ts
import api from './api';
import { User } from '@/types/user.types';
export const getUser = async (id: string): Promise<User> => {
const { data } = await api.get<User>(`/users/${id}`);
return data;
};
6. Typed Redux store (if using Redux Toolkit)
// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import authReducer from '@/features/auth/authSlice';
export const store = configureStore({
reducer: { auth: authReducer },
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// store/hooks.ts — typed versions of useDispatch/useSelector
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
import type { RootState, AppDispatch } from './index';
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
Essential Packages for This Setup
npm install -D typescript @types/react @types/react-dom @types/node
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
Top comments (0)