Make every property optional
type Draft = Partial<User>;
Required<T> goes the other way.
The utility types, narrowing tricks, and compiler flags that come up every day in an ordinary TypeScript codebase.
type Draft = Partial<User>;
Required<T> goes the other way.
type Preview = Pick<User, "id" | "name">;
type Safe = Omit<User, "password">;
const byId: Record<string, User> = {};
Reading byId["missing"] types as User unless noUncheckedIndexedAccess is on.
type Result = ReturnType<typeof fetchUser>;
For an async function wrap it: Awaited<ReturnType<typeof fetchUser>>.
function isCat(pet: Pet): pet is Cat {
return "meow" in pet;
}
After if (isCat(pet)) the compiler treats pet as Cat in that branch.
switch (shape.kind) {
case "circle":
return area(shape);
default: {
const unreachable: never = shape;
return unreachable;
}
}
Add a kind to the union and the assignment to never becomes an error until the switch handles it.
const ROLES = ["user", "admin"] as const;
type Role = (typeof ROLES)[number];
Without as const the array is string[] and Role is just string.
type ConfigKey = keyof typeof config;
const config = { port: 3030, host: "localhost" } satisfies Config;
config.port stays the literal 3030. With const config: Config = ... it widens to number.
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
Works for strings, arrays, and anything else with a length.
npx tsc --noEmit
Point it at another config with -p tsconfig.scripts.json.
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true
}
}
With the second flag, indexing an array or a record gives T | undefined, which is what it is at runtime.