@7ka/tsconfig
v0.1.0
Published
Strict TypeScript configs for Vite, React, and Next.js projects
Maintainers
Readme
@7ka/tsconfig
Shared TypeScript configs for 7ka collective projects.
Install
npm install -D @7ka/tsconfigUsage
React (Vite)
// tsconfig.json
{
"extends": "@7ka/tsconfig/react.json",
"include": ["src"]
}Next.js
// tsconfig.json
{
"extends": "@7ka/tsconfig/next.json",
"include": ["src", "next.config.ts"]
}Base (no DOM — shared logic, utilities)
{
"extends": "@7ka/tsconfig/base.json",
"include": ["src"]
}What's enforced
Compiler
| Option | Value | Why |
|---|---|---|
| target | ES2022 | Modern JS, no over-polyfilling |
| module | ESNext | Native ESM |
| moduleResolution | bundler | Matches Vite resolution, no .js extensions required |
| jsx | react-jsx (react) / preserve (next) | Correct per framework |
Strict flags
| Flag | Why |
|---|---|
| strict | Master switch — enables all core strict checks |
| noUncheckedIndexedAccess | Array index access returns T \| undefined, not just T |
| noImplicitReturns | All code paths must explicitly return |
| noFallthroughCasesInSwitch | Switch cases must break, return, or throw |
| exactOptionalPropertyTypes | Optional ? means absent — not undefined |
| noPropertyAccessFromIndexSignature | Dynamic key access must use bracket notation |
| noImplicitOverride | Class overrides must be marked with override keyword |
| forceConsistentCasingInFileNames | Prevents casing bugs between macOS and Linux CI |
Rule Details
strict
This is not a single rule — it's a bundle. Enabling strict: true turns on all of the following at once: strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitAny, noImplicitThis, and alwaysStrict.
The most important of these is strictNullChecks — without it, null and undefined are silently assignable to every type, which means TypeScript won't tell you when something might not exist. This is the single biggest source of runtime crashes that TypeScript is supposed to prevent. strict mode closes that gap.
noUncheckedIndexedAccess
By default TypeScript assumes that if you access an array by index, you get a value of the array's element type. It does not account for the possibility that the array is empty or the index is out of bounds. This flag adds | undefined to every index access, forcing you to handle the case where nothing is there.
This matters most in React when mapping over API responses — you can't always guarantee the array has content.
const users: User[] = getUsers()
// without flag — TypeScript trusts you, runtime crash if empty
const first = users[0]
console.warn(first.name) // TypeError if array is empty
// with flag — TypeScript is honest
const first = users[0] // typed as User | undefined
if (!first) return
console.warn(first.name) // safenoImplicitReturns
When a function is supposed to return a value, TypeScript will error if any code path exits without an explicit return statement. Without this flag, a missing return silently produces undefined — which then propagates through your app as a hard-to-trace bug.
// bad — second path returns undefined implicitly, TypeScript silent
function getLabel(status: string): string {
if (status === 'active') return 'Active'
// forgot this path — returns undefined at runtime
}
// good — all paths accounted for
function getLabel(status: string): string {
if (status === 'active') return 'Active'
return 'Unknown'
}noFallthroughCasesInSwitch
In a switch statement, if a case block doesn't end with break, return, or throw, execution falls through to the next case. This is almost never intentional and causes logic bugs that are difficult to spot during review.
// bad — 'admin' falls through to 'user', both branches execute
switch (role) {
case 'admin':
grantAdminAccess()
case 'user':
showDashboard()
break
}
// good — each case is isolated
switch (role) {
case 'admin':
grantAdminAccess()
break
case 'user':
showDashboard()
break
}exactOptionalPropertyTypes
When you mark an interface property as optional with ?, TypeScript normally treats it as "this property may be absent or explicitly set to undefined." This flag tightens that — optional means the property is simply absent. Setting it to undefined explicitly becomes a type error.
This distinction matters when working with APIs or databases that treat a missing key differently from a key with a null/undefined value — which is common in REST and GraphQL responses.
interface Config {
timeout?: number
}
// bad — property present but undefined, may behave differently at runtime
const config: Config = { timeout: undefined }
// good — property is simply not there
const config: Config = {}noPropertyAccessFromIndexSignature
When an interface has an index signature (a dynamic [key: string] definition), TypeScript normally lets you access those dynamic keys with dot notation — the same as known, static properties. This flag forces bracket notation for dynamic keys, making it visually obvious at the call site that you're doing a dynamic lookup that may or may not exist.
interface Env {
[key: string]: string
NODE_ENV: string // known, static property
}
// bad — dot notation hides the fact this is a dynamic lookup
const val = env.SOME_KEY
// good — bracket notation signals "this might not be here"
const val = env['SOME_KEY']
// still fine — known property keeps dot notation
const mode = env.NODE_ENVnoImplicitOverride
When a child class defines a method with the same name as a method in its parent class, it silently overrides it. This is fine when intentional but dangerous when it happens by accident — or when the parent method is later renamed or removed, leaving the child with an orphaned method that nobody notices.
This flag requires you to explicitly mark overriding methods with the override keyword. If the parent method is renamed or removed, TypeScript will error on the override annotation immediately.
class Base {
render(): string {
return 'base'
}
}
// bad — silent override, nothing catches it if Base.render is renamed
class Child extends Base {
render(): string {
return 'child'
}
}
// good — explicit intent, TypeScript errors if parent method disappears
class Child extends Base {
override render(): string {
return 'child'
}
}forceConsistentCasingInFileNames
macOS and Windows file systems are case-insensitive — UserCard.tsx and usercard.tsx resolve to the same file. Linux (where CI runs) is case-sensitive — they are different files. This means code that works locally on a Mac can silently fail in CI or production.
This flag makes TypeScript error when an import's casing doesn't match the actual filename on disk, catching the mismatch before it reaches CI.
// file on disk: UserCard.tsx
// bad — works on Mac, breaks on Linux CI
import { UserCard } from './usercard'
// good
import { UserCard } from './UserCard'