Code Review in TypeScript: What to Check and How to Give Feedback in Practice
\n\nCode review is not just a formality, but a powerful tool for improving code quality. In TypeScript, with its rich type system, review can be especially effective. In this article, we'll break down what to pay attention to when reviewing TypeScript code, and how to give feedback that is useful rather than offensive. All examples are based on real code.
\n\n1. Types and Annotations: Avoid `any` and Use Strictness
\n\nThe first thing that stands out in a TypeScript code review is types. TypeScript is designed to catch errors at compile time, so any is a red flag.
What to Check:
\n- \n
- Presence of
any. Everyanymust be justified. If it's a migration from JS, it's okay, but in new code, it's not. \n - Correctness of types. Don't use
stringwhere anenumorunion typeis needed. \n - Excessive typing. TypeScript can infer types. Don't write
const x: number = 5— writeconst x = 5. \n - Strict settings. Check that
tsconfig.jsonhasstrict: true,noImplicitAny,strictNullChecksenabled. \n
Example of Bad Code:
\nfunction processData(data: any) {\n return data.name.toLowerCase();\n}\n\nHow to Fix:
\ninterface User {\n name: string;\n}\n\nfunction processData(data: User): string {\n return data.name.toLowerCase();\n}\n\nHow to Give Feedback:
\nInstead of "You're using any, that's bad," say: "Here any disables type checking. Let's describe the User interface — this will improve code safety and readability."
2. Working with null and undefined: strictNullChecks Saves Lives
\n\nCannot read property of undefined errors are classic. TypeScript with strictNullChecks helps avoid them, but only if the developer correctly handles nullable values.
What to Check:
\n- \n
- Checking for null/undefined before accessing properties. \n
- Using optional chaining (
?.) — it's modern and safe. \n - The
!operator (non-null assertion) — use only if you are 100% sure the value is not null. Better to replace with an explicit check. \n - Correct use of
??(nullish coalescing) instead of||for default values. \n
Example of Bad Code:
\nfunction getCity(user: User | null): string {\n return user!.address!.city; // Dangerous!\n}\n\nHow to Fix:
\nfunction getCity(user: User | null): string {\n return user?.address?.city ?? 'Unknown';\n}\n\nHow to Give Feedback:
\n"Using ! here is risky — if user turns out to be null, an error will occur. Let's replace it with ?. and ?? — it's safe and readable."
3. Generics and Utility Types: Don't Overcomplicate Without Need
\n\nGenerics are a powerful feature, but they are easy to overcomplicate. The reviewer should watch for the balance between flexibility and readability.
\n\nWhat to Check:
\n- \n
- Excessive complexity. If a generic is used only once, it might not be needed. \n
- Correct use of
extends— constrain generics so they don't accept everything. \n - Utility types (
Partial,Pick,Omit,Record) — their use simplifies code. \n - Return types — a function with a generic