Skip to content

Instantly share code, notes, and snippets.

@SirSerje
Last active August 12, 2025 17:37
Show Gist options
  • Select an option

  • Save SirSerje/2479880e641aaa5a17309524cf6fc4e4 to your computer and use it in GitHub Desktop.

Select an option

Save SirSerje/2479880e641aaa5a17309524cf6fc4e4 to your computer and use it in GitHub Desktop.
Typescript Snippets By SirSerje
// Part 1 — Senior Dev Gatekeeper
// 1) `satisfies` (TS 4.9)
// Without
type User = { id: number; role: 'admin' | 'user' };
const u1: User = { id: 1, role: 'admin', extra: true as any }; // error
// With
const u2 = { id: 1, role: 'admin' } satisfies User;
---
window.customData = { theme: 'dark' }; // TS2339
console.log(window.customData?.theme); // TS2339
// ❌ BEFORE — No augmentation, causes TS2339
window.customData = { theme: 'dark' };
console.log(window.customData?.theme);
// ✅ AFTER — In a .d.ts file (e.g., global.d.ts)
export {}; // ensures this is treated as a module
declare global {
interface Window {
customData?: { theme: string };
}
}
// main.ts
window.customData = { theme: 'dark' };
console.log(window.customData?.theme); // works
---
// 3) `Record` coverage with `satisfies`
// Without
type Route = 'home' | 'profile' | 'settings';
const paths1: { [k: string]: string } = { home: '/', profile: '/p' }; // nah
// With
const paths2 = {
home: '/',
profile: '/p',
settings: '/s'
} satisfies Record<Route, string>; // nice
// Part 2 — Code Cleaner
// 1) Generics
// ❌ no generics
function wrapInArray1(value: number): number[] {
return [value];
}
const a1 = wrapInArray1(5); // ok
// const b1 = wrapInArray1("hi"); // ❌ error
// ✅ with <T> - flexible
function wrapInArray2<T>(value: T): T[] {
return [value];
}
const a2 = wrapInArray2(5); // number[]
const b2 = wrapInArray2("hi"); // string[]
const c2 = wrapInArray2({ id: 1 }); // { id: number }[]
// 2) `keyof typeof`
// ❌ no checks for keys
type StatusKey = string;
const statuses: Record<StatusKey, number> = {
open: 1,
closed: 2,
pending: 3,
archive: 4, // ← throws error as expected
};
// With ✅
const allowedStatuses = { open: 1, closed: 2, pending: 3 } as const;
type StatusKey = keyof typeof allowedStatuses;
// 'open' | 'closed' | 'pending'
const statuses: Record<StatusKey, number> = {
open: 10,
closed: 20,
pending: 30,
// archive: 40, // ❌ TS error — no key
};
// 3) User-defined type guards (`is`)
// Without
function getId1(x: unknown) { return (x as any)?.id ?? null; }
// With
type WithId = { id: string | number };
function hasId(x: unknown): x is WithId {
return typeof x === 'object' && x !== null && 'id' in x;
}
function getId2(x: unknown) { return hasId(x) ? x.id : null; }
// Part 3 — Absolutely Nuts
// 1) Template literal types
// Without
type EventName1 = 'user_created' | 'user_deleted' | 'order_created' | 'order_deleted';
// With
type Entity = 'user' | 'order';
type Action = 'created' | 'deleted';
type EventName2 = `${Entity}_${Action}`;
// 2) `as const` literal inference
// ❌ Without
const roles1 = ['admin', 'user', 'guest'];
// roles1: string[]
type Role1 = typeof roles1[number];
// Role1 = string ← losing specifics
// ✅ With
const roles2 = ['admin', 'user', 'guest'] as const;
// roles2: readonly ["admin", "user", "guest"]
type Role2 = typeof roles2[number];
// Role2 = "admin" | "user" | "guest" ← exact values
// 3) Decorators (TS experimental)
// Without
class Repo1 {
getById(id: string) { return { id }; }
}
// With
function Log(_t: any, k: string, d: PropertyDescriptor) {
const orig = d.value;
d.value = function (...args: any[]) {
console.time(k);
const r = orig.apply(this, args);
console.timeEnd(k);
return r;
};
}
class Repo2 {
@Log
getById(id: string) { return { id }; }
}
// Part 4 — Beyond AI
// 1) Simple `infer`
// Without
type Elem1<T> = T; // no flex(ibility)
// With
type ElementType<T> = T extends (infer U)[] ? U : T;
type A = ElementType<string[]>; // string
type B = ElementType<number>; // number
// 2) Type guard (`is`)
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function getSmallPet(): Fish | Bird {
return Math.random() > 0.5
? { swim: () => console.log('Swimming...') }
: { fly: () => console.log('Flying...') };
}
// 😪 clumsy
let pet = getSmallPet();
if ((pet as Fish)?.swim) {
(pet as Fish).swim();
} else {
(pet as Bird).fly();
}
// 😏 better way
function isFish(
pet: Fish | Bird
): pet is Fish {
return (pet as Fish).swim !== undefined;
}
if(isFish(pet)) {
pet.swim()
} else {
pet.fly()
}
//😃
type User2 = { name: string };
// ❌
function ensureUser1(x: any) {
if (!x?.name) throw new Error('bad');
}
function greetBad(u: unknown) {
ensureUser1(u);
// ❌ TS: 'u' is unknown
u.name.toUpperCase();
}
// ✅ using assertion:
function assertUser(x: any): asserts x is User2 {
if (typeof x?.name !== 'string') throw new Error('bad');
}
function greetGood(u: unknown) {
assertUser(u); // TS knows its User2
u.name.toUpperCase(); // ✅ OK
}
// Part 5 — Squeaky Clean Finale
// 1) `readonly` arrays/tuples
// Without
function useA1(nums: number[]) { nums.push(99); }
// With
function useA2(nums: readonly number[]) { /* no mutation allowed */ }
// 2) Utility types `Pick` / `Omit`
// Without
type UserFull = { id: string; name: string; email: string; age: number };
type UserPublic1 = { id: string; name: string };
// With
type UserPublic2 = Pick<UserFull, 'id' | 'name'>;
type UserPrivate2 = Omit<UserFull, 'email'>;
// 3) Namespaces + untyped 3rd-party lib wrapper
declare const ThirdPartyAPI: any;
// Without
const raw = ThirdPartyAPI.send({ sz: 12, tp: ['x'] });
// With
namespace Vendor {
export namespace Orders {
export type CreateInput = { size: number; tags: string[] };
export type CreateResult = { id: string; eta: number };
export function create(input: CreateInput): CreateResult {
return ThirdPartyAPI.send({ sz: input.size, tp: input.tags });
}
}
}
const order = Vendor.Orders.create({ size: 12, tags: ['x'] });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment