Q37 of 38 · TypeScript
How do you standardize TypeScript usage across a large QA automation team?
Short answer
Short answer: Establish a shared `tsconfig` base, enforced ESLint rules, a typed utility library, code review guidelines for type safety, and team training. Treat type quality as a code quality metric — track `any` count and type coverage in CI to prevent erosion over time.
Detail
Standardization across a team means making the right TypeScript patterns the path of least resistance.
Shared tsconfig base:
Publish a @yourorg/tsconfig NPM package with base settings. Individual projects extend it: { "extends": "@yourorg/tsconfig/playwright" }. Centralizes version bumps and strictness decisions.
ESLint ruleset as an NPM package:
Same pattern — @yourorg/eslint-config-typescript with rules for: no-explicit-any, no-unsafe-assignment, no-unused-vars, naming conventions, and import ordering. PR checks fail on violations.
Typed utility library:
Common types (TestUser, ApiResponse<T>, FixtureOptions) and helpers (makeUserId, parseApiDate) published as an internal package. Teams pull it in rather than re-implementing.
Code review guidelines: A written "TypeScript review checklist" — is unknown used at boundaries? Are return types explicit on public methods? Are discriminated unions used for state? This makes reviews consistent regardless of reviewer TypeScript depth.
Training plan:
- Pairing on complex generic or conditional types
- Monthly TypeScript "type of the month" pattern shared in Slack
- Internal workshop on branded types, utility types, advanced patterns
Metrics:
- Track
anycount per repo in CI (decrease = good signal) - Type coverage score (
typescript-coverage-reporttool) - New-hire onboarding time to first passing TypeScript PR
// EXAMPLE
// packages/tsconfig/playwright.json — shared base
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"skipLibCheck": true,
"esModuleInterop": true
}
}
// In a test project
// tsconfig.json
{
"extends": "@yourorg/tsconfig/playwright",
"compilerOptions": {
"baseUrl": ".",
"paths": { "@pages/*": ["./pages/*"] }
},
"include": ["tests/**/*", "pages/**/*"]
}
// CI gate — fail on any new 'any' over baseline
// package.json
{
"scripts": {
"typecheck": "tsc --noEmit",
"type-coverage": "type-coverage --at-least 95 --strict"
}
}