From Airbnb Style Guide
Overview¶
This guide helps JavaScript and React developers transition from the Airbnb JavaScript Style Guide to the DevOps Engineering Style Guide with a focus on TypeScript adoption. While Airbnb's guide is excellent for JavaScript, our guide adds TypeScript type safety, DevOps-oriented enhancements, and infrastructure automation requirements.
What This Guide Covers¶
- JavaScript → TypeScript migration path
- Airbnb conventions vs. DevOps Engineering standards
- Type safety and static analysis requirements
- Modern tooling updates (ESLint, Prettier, TypeScript)
- React best practices alignment
- Step-by-step migration checklist
Who Should Use This Guide¶
- Teams currently following Airbnb JavaScript/React Style Guide
- JavaScript projects adopting TypeScript
- React applications transitioning to type-safe development
- Organizations standardizing on TypeScript for DevOps tooling
Quick Compatibility Summary¶
graph LR
Airbnb[Airbnb JavaScript<br/>Style Guide] --> JavaScript[JavaScript Patterns<br/>Mostly Compatible]
Airbnb --> TypeScript[TypeScript Migration<br/>Type Safety Required]
Airbnb --> DevOps[DevOps Enhancements<br/>Tooling & Standards]
JavaScript --> Migration[Migration Path]
TypeScript --> Migration
DevOps --> Migration
Migration --> Dukes[DevOps Engineering<br/>TypeScript Guide]
style JavaScript fill:#e8f5e9
style TypeScript fill:#fff3e0
style DevOps fill:#f3e5f5
style Dukes fill:#e3f2fd
What Stays the Same¶
Both Airbnb and DevOps Engineering guides share these JavaScript best practices:
Core Principles ✅¶
| Aspect | Airbnb | Our Guide |
|---|---|---|
| Prefer const | ✅ | ✅ Same |
| No var | ✅ | ✅ Same |
| Arrow functions | ✅ | ✅ Same |
| Template literals | ✅ | ✅ Same |
| Destructuring | ✅ | ✅ Same |
| Spread operator | ✅ | ✅ Same |
| Object shorthand | ✅ | ✅ Same |
| Array methods | ✅ | ✅ Same (map, filter, reduce) |
| Async/await | ✅ | ✅ Same (preferred over promises) |
| Module imports | ✅ | ✅ Same (ES6 modules) |
Naming Conventions ✅¶
| Element | Convention | Airbnb | Our Guide |
|---|---|---|---|
| Variables | camelCase |
✅ | ✅ Same |
| Constants | UPPER_SNAKE_CASE |
✅ | ✅ Same |
| Functions | camelCase |
✅ | ✅ Same |
| Classes | PascalCase |
✅ | ✅ Same |
| React Components | PascalCase |
✅ | ✅ Same |
| Private (convention) | _leadingUnderscore |
✅ | ✅ Same |
| Files | camelCase or PascalCase |
✅ | ✅ Same |
Code Style ✅¶
- Indentation: 2 spaces (both guides)
- Semicolons: Required (both guides)
- Quotes: Single quotes for strings (both guides)
- Trailing commas: Encouraged (both guides)
- Max line length: 100 characters (both guides)
What Changes: JavaScript → TypeScript¶
1. Type Annotations: Optional → Required¶
Airbnb (JavaScript): No type system (relies on JSDoc comments) Our Guide (TypeScript): Type annotations required for all functions, variables, and parameters
Why: TypeScript provides compile-time type checking, prevents runtime errors, improves IDE support, and serves as inline documentation for DevOps automation code.
Migration:
// Airbnb JavaScript - JSDoc for types
/**
* Calculate total price with tax
* @param {number} price - Base price
* @param {number} taxRate - Tax rate (0-1)
* @returns {number} Total price
*/
function calculateTotal(price, taxRate) {
return price * (1 + taxRate);
}
// Our Guide TypeScript - Type annotations
function calculateTotal(price: number, taxRate: number): number {
return price * (1 + taxRate);
}
Action Required:
- Add TypeScript type annotations to all functions
- Replace JSDoc types with TypeScript types
- Define interfaces for complex objects
- Use type aliases for union types
2. Interface Definitions: None → Required¶
Airbnb (JavaScript): PropTypes for React, no formal interfaces Our Guide (TypeScript): Interfaces/types required for all data structures
Migration:
// Airbnb JavaScript with PropTypes
import PropTypes from 'prop-types';
function UserCard({ user }) {
return <div>{user.name}</div>;
}
UserCard.propTypes = {
user: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired
}).isRequired
};
// Our Guide TypeScript with interfaces
interface User {
id: number;
name: string;
email: string;
}
interface UserCardProps {
user: User;
}
function UserCard({ user }: UserCardProps): JSX.Element {
return <div>{user.name}</div>;
}
Action Required:
- Convert PropTypes to TypeScript interfaces
- Define interfaces for all data structures
- Use type inference where obvious
- Export interfaces for shared types
3. React Component Types: Implicit → Explicit¶
Airbnb (JavaScript): Functional components, PropTypes validation Our Guide (TypeScript): Typed functional components with React.FC or explicit types
Migration:
// Airbnb JavaScript
const Button = ({ onClick, children, disabled = false }) => (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
);
Button.propTypes = {
onClick: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
disabled: PropTypes.bool
};
// Our Guide TypeScript - Option 1: Explicit types (recommended)
interface ButtonProps {
onClick: () => void;
children: React.ReactNode;
disabled?: boolean;
}
const Button = ({ onClick, children, disabled = false }: ButtonProps): JSX.Element => (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
);
// Our Guide TypeScript - Option 2: React.FC (acceptable)
const Button: React.FC<ButtonProps> = ({ onClick, children, disabled = false }) => (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
);
Action Required:
- Define prop interfaces for all components
- Remove PropTypes dependencies
- Add return type annotations
- Use optional properties (
?) for optional props
4. Enum Usage: None → Recommended¶
Airbnb (JavaScript): Object constants or string literals Our Guide (TypeScript): Enums or const enums for fixed sets of values
Migration:
// Airbnb JavaScript - Object constants
const UserRole = {
ADMIN: 'admin',
USER: 'user',
GUEST: 'guest'
};
function checkPermission(role) {
return role === UserRole.ADMIN;
}
// Our Guide TypeScript - Enums
enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest'
}
function checkPermission(role: UserRole): boolean {
return role === UserRole.Admin;
}
// Or const enum for better tree-shaking
const enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest'
}
Action Required:
- Convert object constants to enums
- Use const enums for better performance
- Define enum types for fixed value sets
5. Null/Undefined Handling: Loose → Strict¶
Airbnb (JavaScript): Loose equality, runtime checks Our Guide (TypeScript): Strict null checks, union types with null/undefined
Migration:
// Airbnb JavaScript
function getUserName(user) {
if (!user || !user.name) {
return 'Anonymous';
}
return user.name;
}
// Our Guide TypeScript
interface User {
name: string;
}
function getUserName(user: User | null | undefined): string {
if (!user?.name) {
return 'Anonymous';
}
return user.name;
}
// Or with optional chaining and nullish coalescing
function getUserName(user?: User): string {
return user?.name ?? 'Anonymous';
}
Action Required:
- Enable
strictNullChecksin tsconfig.json - Use optional chaining (
?.) and nullish coalescing (??) - Explicitly type nullable values with
| null | undefined - Handle null/undefined cases explicitly
6. Generic Types: None → Used Extensively¶
Airbnb (JavaScript): No generics concept Our Guide (TypeScript): Generics for reusable, type-safe functions
Migration:
// Airbnb JavaScript
function getFirstItem(array) {
return array.length > 0 ? array[0] : null;
}
// Our Guide TypeScript - Generics
function getFirstItem<T>(array: T[]): T | null {
return array.length > 0 ? array[0] : null;
}
// Usage with type inference
const firstNumber = getFirstItem([1, 2, 3]); // Type: number | null
const firstName = getFirstItem(['a', 'b']); // Type: string | null
Action Required:
- Add generic type parameters to reusable functions
- Use generics for containers (arrays, maps, sets)
- Define generic interfaces for flexible data structures
Tool Configuration Migration¶
From JavaScript to TypeScript Build Tools¶
Remove JavaScript-only Tools¶
## Uninstall PropTypes (replaced by TypeScript)
npm uninstall prop-types
## Remove Babel if only used for type checking
npm uninstall @babel/preset-typescript
Install TypeScript Tooling¶
## Install TypeScript and related tools
npm install --save-dev typescript
npm install --save-dev @types/react @types/react-dom @types/node
npm install --save-dev ts-node tsx
TypeScript Configuration¶
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"allowJs": true,
"checkJs": false,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
ESLint Configuration Updates¶
Update ESLint for TypeScript¶
## Install TypeScript ESLint
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
Update .eslintrc.json¶
{
"extends": [
"airbnb",
"airbnb/hooks",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
},
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint", "react", "react-hooks"],
"rules": {
"react/jsx-filename-extension": [1, { "extensions": [".tsx"] }],
"react/prop-types": "off",
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "error",
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"tsx": "never"
}
]
},
"settings": {
"import/resolver": {
"typescript": {}
}
}
}
Prettier Configuration¶
Both Airbnb and our guide work well with Prettier. Update .prettierrc:
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"arrowParens": "always",
"endOfLine": "lf"
}
Migration Checklist¶
Phase 1: Setup TypeScript Infrastructure¶
- [ ] Install TypeScript and dependencies
npm install --save-dev typescript
npm install --save-dev @types/react @types/react-dom @types/node
- [ ] Create
tsconfig.json - Enable strict mode
- Configure module resolution
-
Set target to ES2020+
-
[ ] Update build scripts in
package.json
{
"scripts": {
"build": "tsc",
"dev": "tsx src/index.ts",
"type-check": "tsc --noEmit"
}
}
- [ ] Install TypeScript ESLint
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
- [ ] Update ESLint configuration
- Add TypeScript parser
- Add TypeScript plugin
- Update rules for TypeScript
Phase 2: Incremental File Migration¶
- [ ] Rename files from .js/.jsx to .ts/.tsx
- Start with utility functions (
.js→.ts) - Then components (
.jsx→.tsx) -
Work from leaf modules to entry points
-
[ ] Enable
allowJsin tsconfig.json - Allows gradual migration
-
TypeScript and JavaScript can coexist
-
[ ] Address type errors incrementally
- Fix one module at a time
- Use
// @ts-ignoresparingly for tough cases - Create issue tracking for remaining
@ts-ignore
Phase 3: Add Type Definitions¶
- [ ] Convert PropTypes to TypeScript interfaces
// Before
Component.propTypes = {
user: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string
})
};
// After
interface User {
id: number;
name: string;
}
interface ComponentProps {
user: User;
}
- [ ] Add explicit return types to functions
function calculateTotal(price: number, tax: number): number {
return price * (1 + tax);
}
- [ ] Define interfaces for API responses
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
- [ ] Create type definitions for external libraries
- Install
@types/*packages - Create custom
.d.tsfiles if needed
Phase 4: React Component Migration¶
- [ ] Update functional components with types
const Button: React.FC<ButtonProps> = ({ onClick, children }) => (
<button onClick={onClick}>{children}</button>
);
- [ ] Add types to hooks
const [count, setCount] = useState<number>(0);
const ref = useRef<HTMLDivElement>(null);
const context = useContext<AuthContextType>(AuthContext);
- [ ] Type event handlers
const handleClick = (event: React.MouseEvent<HTMLButtonElement>): void => {
console.log(event.currentTarget);
};
- [ ] Remove PropTypes dependencies
npm uninstall prop-types
Phase 5: Advanced TypeScript Features¶
- [ ] Use generics for reusable components
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <>{items.map(renderItem)}</>;
}
- [ ] Add discriminated unions for state
type LoadingState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; error: string };
- [ ] Use utility types
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
type UserKeys = keyof User;
type UserName = Pick<User, 'name'>;
Phase 6: Testing Updates¶
- [ ] Install TypeScript testing types
npm install --save-dev @types/jest @types/testing-library__react
-
[ ] Update test files to
.test.tsor.test.tsx -
[ ] Add types to test utilities
const renderWithProviders = (
ui: React.ReactElement,
options?: RenderOptions
): RenderResult => {
return render(ui, { wrapper: AllTheProviders, ...options });
};
Phase 7: CI/CD Integration¶
- [ ] Add TypeScript check to CI pipeline
- name: Type check
run: npm run type-check
- [ ] Configure TypeScript strict mode gradually
- Start with
strictNullChecks: true - Enable
noImplicitAny: true -
Eventually enable all strict flags
-
[ ] Add type coverage tracking
npx type-coverage --at-least 95
Phase 8: Documentation and Training¶
- [ ] Update README with TypeScript instructions
- Document type-checking commands
-
Explain tsconfig.json settings
-
[ ] Create TypeScript coding guidelines
- When to use interfaces vs types
- Generic type naming conventions
-
Any vs unknown usage
-
[ ] Team training on TypeScript
- Share this migration guide
- Conduct TypeScript workshop
- Set up pair programming for migration
Common Migration Pitfalls¶
1. Overuse of any Type¶
Problem: Using any everywhere defeats the purpose of TypeScript.
Solution: Use specific types or unknown for truly dynamic values.
// Avoid
function processData(data: any): any {
return data.value;
}
// Better - use generics or specific types
function processData<T extends { value: string }>(data: T): string {
return data.value;
}
// Or for truly unknown data
function processData(data: unknown): string {
if (typeof data === 'object' && data !== null && 'value' in data) {
return String((data as { value: unknown }).value);
}
throw new Error('Invalid data structure');
}
2. Ignoring Null/Undefined¶
Problem: Not handling null/undefined cases explicitly.
Solution: Enable strict null checks and handle edge cases.
// Risky
function getUserEmail(user: User): string {
return user.email; // What if user.email is undefined?
}
// Safe
function getUserEmail(user: User): string | undefined {
return user.email;
}
// Or with default
function getUserEmail(user: User): string {
return user.email ?? 'no-email@example.com';
}
3. Type Assertion Abuse¶
Problem: Using as type assertions to bypass type checking.
Solution: Fix the underlying type issue instead of asserting.
// Risky
const button = document.getElementById('btn') as HTMLButtonElement;
button.click(); // What if it's not a button?
// Safer
const element = document.getElementById('btn');
if (element instanceof HTMLButtonElement) {
element.click();
}
4. Missing Generic Constraints¶
Problem: Generics without constraints allow any type.
Solution: Add constraints to generics for type safety.
// Too permissive
function getProperty<T>(obj: T, key: string) {
return obj[key]; // Type error: key not constrained
}
// Better - constrain the key
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
5. PropTypes Not Fully Converted¶
Problem: Leaving PropTypes alongside TypeScript types.
Solution: Remove PropTypes after adding TypeScript interfaces.
// Redundant
interface ButtonProps {
onClick: () => void;
label: string;
}
const Button: React.FC<ButtonProps> = ({ onClick, label }) => (
<button onClick={onClick}>{label}</button>
);
// Don't do this - remove PropTypes
Button.propTypes = {
onClick: PropTypes.func.isRequired,
label: PropTypes.string.isRequired
};
Gradual Adoption Strategy¶
Week 1-2: Foundation¶
- Set up TypeScript compiler and configuration
- Install type definitions for dependencies
- Configure ESLint for TypeScript
- Update build pipeline to include type checking
Week 3-4: Core Utilities¶
- Migrate utility functions (
.js→.ts) - Add type definitions for common data structures
- Create shared type definition files
- No React component migration yet
Week 5-6: React Components (Phase 1)¶
- Start with leaf components (no dependencies)
- Convert PropTypes to TypeScript interfaces
- Add prop type definitions
- Update component signatures
Week 7-8: React Components (Phase 2)¶
- Migrate higher-level components
- Add context provider types
- Type custom hooks
- Add event handler types
Week 9-10: State Management¶
- Type Redux/Zustand/Context stores
- Add action and reducer types
- Type selectors and middleware
- Ensure type safety in state updates
Week 11-12: API & Async Code¶
- Add types for API responses
- Type async functions and promises
- Add types for fetch/axios calls
- Create API client interfaces
Month 4+: Polish and Strictness¶
- Enable all strict TypeScript flags
- Remove all
// @ts-ignorecomments - Achieve 95%+ type coverage
- Zero TypeScript errors project-wide
Success Metrics¶
| Metric | Target | Measurement |
|---|---|---|
| Type Coverage | 95%+ | npx type-coverage |
| TypeScript Errors | 0 | tsc --noEmit |
any Usage |
<1% | ESLint rule |
| Strict Mode | Enabled | tsconfig.json |
| PropTypes Removed | 100% | No prop-types in deps |
| Test Coverage | 80%+ | Jest with types |
Side-by-Side Comparison: Airbnb vs. DevOps Engineering¶
| Aspect | Airbnb JavaScript | DevOps Engineering TypeScript |
|---|---|---|
| Language | JavaScript (ES6+) | TypeScript |
| Type System | None (JSDoc comments) | Static types required |
| Type Checking | Runtime (PropTypes) | Compile-time |
| React Prop Validation | PropTypes | TypeScript interfaces |
| Null Safety | Runtime checks | Compile-time strictNullChecks |
| IDE Support | Good | Excellent (autocomplete, refactoring) |
| Error Detection | Runtime | Compile-time |
| Generics | Not available | Used extensively |
| Interfaces | Not available | Required for data structures |
| Enum Support | Object constants | Native enums |
| Build Step | Babel | TypeScript compiler |
| File Extensions | .js, .jsx |
.ts, .tsx |
| Linter | ESLint (JavaScript) | ESLint + TypeScript plugin |
Support and Resources¶
Documentation References¶
- TypeScript Style Guide - Full DevOps Engineering TypeScript standards
- Testing Strategies - TypeScript testing patterns
- IDE Integration Guide - VS Code TypeScript setup
- GitHub Actions Guide - TypeScript CI/CD workflows
Tool Documentation¶
- TypeScript Handbook - Official TypeScript docs
- TypeScript ESLint - ESLint for TypeScript
- React TypeScript Cheatsheet - React with TypeScript
- @types packages - Type definitions
External References¶
- Airbnb JavaScript Style Guide - Original Airbnb guide
- Airbnb React Style Guide - React conventions
- TypeScript Migration Guide - Official migration docs
Conclusion¶
Migrating from Airbnb JavaScript Style Guide to DevOps Engineering TypeScript Style Guide brings:
✅ Type Safety - Catch errors at compile-time instead of runtime ✅ Better IDE Support - Enhanced autocomplete, refactoring, and navigation ✅ Self-Documenting Code - Types serve as inline documentation ✅ Refactoring Confidence - Type system ensures changes don't break contracts ✅ DevOps Ready - Type safety essential for infrastructure automation ✅ Modern Tooling - Latest TypeScript features and ecosystem ✅ Scalability - Type system scales with codebase growth
While Airbnb's JavaScript guide is excellent, TypeScript adds critical type safety and developer experience improvements essential for modern DevOps workflows, infrastructure automation, and large-scale applications.
Questions or need help? Open an issue or consult the Getting Started Guide.