Free tools Windows power users keep installed
One-click scans. No signup required.
Useful TypeScript patterns make uncertainty visible, represent each state clearly, and help the compiler catch omissions. If you already read TypeScript, these seven practices offer a practical answer to the question, “What language features or tricks are useful day to day?” They are a set of techniques, not a ranking or a claim that every project should adopt them without adjustment.
1. Enable strict null checks when your project can support them
A value that might be null or undefined is a different situation from a value that is always present. With strictNullChecks enabled, TypeScript requires code to account for those possibilities instead of treating nullable values as interchangeable with ordinary values.
As an Amazon Associate I earn from qualifying purchases.
For example, a lookup may fail to find a user:
function findUser(id: string): User | undefined {
// ...
}
const user = findUser("u-123");
console.log(user.name); // Error: user might be undefined
Check the result before accessing it:
const user = findUser("u-123");
if (user !== undefined) {
console.log(user.name);
}
The TypeScript Handbook encourages enabling strictNullChecks where possible. In an older codebase, however, turning it on can expose many places that rely on values being present without proving it. Treat adoption as a project change: assess the resulting errors, fix or deliberately handle uncertain values, and consider the migration effort before changing the setting.
2. Narrow uncertain values before using them
When a value has more than one possible type, use ordinary JavaScript checks to establish which case applies. TypeScript follows control flow and narrows the type inside the relevant branch.
#1 Best Overall
Check union members with a type test
A function receiving either a string or a number can handle the two types explicitly:
function format(value: string | number): string {
if (typeof value === "string") {
return value.trim();
}
return value.toFixed(2);
}
Within the first branch, TypeScript knows value is a string; after that branch, it knows the remaining possibility is a number.
Check nullable values before access
function displayName(name: string | null): string {
if (name === null) {
return "Anonymous";
}
return name.toUpperCase();
}
The check makes the function’s handling of the missing case explicit. Similar narrowing can come from property checks, truthiness checks, loops, and other control-flow branches. Pick a check that matches the actual domain: a truthiness test, for instance, also treats an empty string as false, which may not mean “missing” in your application.
Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
3. Represent distinct states with discriminated unions
When an object can represent different states, model each state as its own object shape and give every shape a shared literal-valued discriminant. This keeps state-specific data attached to the state where it is valid.
Before: loosely related optional fields
type RequestResult = {
loading?: boolean;
data?: User[];
error?: string;
};
This shape permits combinations that may not make sense, such as loading and success data at the same time, or an error without a clear indication of the request state.
After: one shape per state
type RequestResult =
| { state: "loading" }
| { state: "success"; data: User[] }
| { state: "failed"; error: string };
Now a successful result carries data, a failed result carries an error, and loading needs neither. Checking state narrows the value to the matching member:
function render(result: RequestResult): string {
if (result.state === "success") {
return `${result.data.length} users`;
}
if (result.state === "failed") {
return result.error;
}
return "Loading…";
}
The discriminant is useful when the alternatives are genuinely different states, not merely optional details on one stable object.
4. Make union handling exhaustive
Once a union defines the possible cases, handle them all at important decision points. An exhaustive switch paired with a never check can make adding a new variant surface as a type error where the union is handled.
type RequestResult =
| { state: "loading" }
| { state: "success"; data: User[] }
| { state: "failed"; error: string };
function describe(result: RequestResult): string {
switch (result.state) {
case "loading":
return "Loading";
case "success":
return `Loaded ${result.data.length} users`;
case "failed":
return `Failed: ${result.error}`;
default: {
const unreachable: never = result;
return unreachable;
}
}
}
If a new state such as cancelled is added to RequestResult, the assignment to never no longer type-checks until the switch accounts for it. This check applies to the declared union during type checking; it does not verify that external data actually conforms to that declaration.
5. Use generics to preserve useful type relationships
A generic is valuable when a function works across multiple types while preserving a relationship between its input and output. It is not a reason to make every function generic.
Before: the output loses the input type
function identity(value: unknown): unknown {
return value;
}
const id = identity(42); // unknown
After: carry the type through
function identity<T>(value: T): T {
return value;
}
const id = identity(42); // number
The type parameter links the argument and return value: callers can pass different types, and the returned value retains the corresponding type information. Use a generic when that relationship helps callers; if a function only accepts one known type, a direct type may be clearer.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches6. Use utility types for common transformations
TypeScript’s built-in utility types express recurring type transformations without requiring a new handwritten type for every variation. Two common examples are Partial<T>, which makes the properties of T optional, and Pick<T, K>, which selects a subset of its properties.
Best Value
type User = {
id: string;
name: string;
email: string;
};
type UserUpdate = Partial<User>;
type UserSummary = Pick<User, "id" | "name">;
UserUpdate can describe a partial update, while UserSummary describes a shape containing only the selected fields. These types transform static type information; they do not remove fields from an object or validate data at runtime. Choose a utility type when its meaning fits the domain, and use a named type when the result needs its own semantics or rules.
7. Keep type assertions rare and deliberate
A type assertion tells TypeScript to trust that a value has a particular type. It does not check or reshape the value at runtime. The TypeScript Handbook puts it this way: “A type assertion is like a type cast in other languages, but it performs no special checking or restructuring of data.”
Do not cast untrusted data into shape
const user = JSON.parse(responseText) as User;
This assertion does not establish that the response contains a valid User. If data comes from a network, file, or other untrusted boundary, validate its runtime shape before relying on it.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Use an assertion only when you can justify the trust
An assertion can be appropriate when a check or invariant exists that TypeScript cannot represent, and the programmer can explain why the value meets the asserted type. If the justification is simply that an error disappears, the assertion may be hiding a real mismatch. Prefer narrowing or a runtime validation when the value’s shape is uncertain.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




