{"id":51110264,"url":"https://github.com/AllThingsSmitty/typescript-tips-everyone-should-know","last_synced_at":"2026-07-12T23:00:40.048Z","repository":{"id":360233666,"uuid":"1249199073","full_name":"AllThingsSmitty/typescript-tips-everyone-should-know","owner":"AllThingsSmitty","description":"A curated collection of practical TypeScript patterns that improve safety, readability, maintainability, and developer experience.","archived":false,"fork":false,"pushed_at":"2026-06-03T00:31:27.000Z","size":11,"stargazers_count":72,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-06-03T02:10:13.402Z","etag":null,"topics":["best-practices","clean-code","code-quality","developer-experience","front-end","frontend","generics","javascript","maintainability","runtime-validation","static-analysis","type-safety","typescript"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AllThingsSmitty.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-25T13:00:48.000Z","updated_at":"2026-06-03T01:30:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AllThingsSmitty/typescript-tips-everyone-should-know","commit_stats":null,"previous_names":["allthingssmitty/typescript-tips-everyone-should-know"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AllThingsSmitty/typescript-tips-everyone-should-know","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ftypescript-tips-everyone-should-know","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ftypescript-tips-everyone-should-know/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ftypescript-tips-everyone-should-know/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ftypescript-tips-everyone-should-know/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AllThingsSmitty","download_url":"https://codeload.github.com/AllThingsSmitty/typescript-tips-everyone-should-know/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ftypescript-tips-everyone-should-know/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35404819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-12T02:00:06.386Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["best-practices","clean-code","code-quality","developer-experience","front-end","frontend","generics","javascript","maintainability","runtime-validation","static-analysis","type-safety","typescript"],"created_at":"2026-06-24T17:00:25.349Z","updated_at":"2026-07-12T23:00:40.042Z","avatar_url":"https://github.com/AllThingsSmitty.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"# TypeScript Tips Everyone Should Know\n\nA curated collection of practical TypeScript patterns that improve safety, readability, maintainability, and developer experience.\n\nMost of these are small individually. Together, they dramatically change how TypeScript code feels to work in.\n\n## Table of Contents\n\n1. [Prefer `unknown` Over `any`](#prefer-unknown-over-any)\n2. [Let Type Inference Do the Work](#let-type-inference-do-the-work)\n3. [Prefer `satisfies` Over `as`](#prefer-satisfies-over-as)\n4. [Derive Types From Values](#derive-types-from-values-instead-of-duplicating-them)\n5. [Make Invalid States Impossible to Represent](#make-invalid-states-impossible-to-represent)\n6. [Use Exhaustive Checks With `never`](#use-exhaustive-checks-with-never)\n7. [Use `as const` for Constants](#use-as-const-for-configuration-and-constants)\n8. [Use Type Predicates](#use-type-predicates-for-reusable-narrowing)\n9. [Build Types From Existing Types](#build-new-types-from-existing-types)\n10. [Validate External Data at Runtime](#validate-external-data-at-runtime)\n11. [Avoid `enum` in Most Cases](#avoid-enum-in-most-cases)\n12. [Prefer Inferable Generics](#prefer-generics-that-infer-automatically)\n13. [Enable Strict Compiler Options](#turn-on-the-strict-compiler-options)\n14. [Learn Template Literal Types](#learn-template-literal-types)\n15. [Type Safety ≠ Runtime Safety](#type-safe-does-not-mean-runtime-safe)\n\n### Prefer `unknown` Over `any`\n\nA lot of type safety starts here.\n\n`unknown` forces you to prove what a value is before using it. `any` skips the type system entirely, allowing unsafe operations to spread through your code.\n\n```ts\nfunction parse(data: unknown) {\n  if (typeof data === \"string\") {\n    return data.toUpperCase();\n  }\n}\n```\n\n#### Why it matters\n\n- Forces validation before use\n- Preserves type safety\n- Prevents unsafe type leakage\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Let Type Inference Do the Work\n\nThe best TypeScript code often relies on inference instead of repeating information the compiler already knows.\n\n```ts\nconst name = \"Ada\";\n```\n\nInstead of:\n\n```ts\nconst name: string = \"Ada\";\n```\n\n#### Over-annotation\n\n- Widens types\n- Hurts inference\n- Creates maintenance overhead\n\nInference tends to scale better than annotation.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Prefer `satisfies` Over `as`\n\nOne of the most important modern TypeScript features.\n\n```ts\nconst routes = {\n  home: \"/\",\n  about: \"/about\",\n} satisfies Record\u003cstring, string\u003e;\n```\n\nInstead of:\n\n```ts\nconst routes = {\n  home: \"/\",\n  about: \"/about\",\n} as Record\u003cstring, string\u003e;\n```\n\n`satisfies` checks that a value matches a type while preserving its inferred type.\n\nUse `satisfies` when validating object shapes. Reserve `as` for cases where you're expressing information the compiler genuinely can't infer.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Derive Types From Values Instead of Duplicating Them\n\nOne of the biggest TypeScript mindset shifts.\n\n```ts\nconst roles = [\"admin\", \"user\", \"guest\"] as const;\n\ntype Role = (typeof roles)[number];\n```\n\nThis creates a single source of truth. If the runtime values change, the type updates automatically, eliminating duplication and preventing the two from drifting apart.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Make Invalid States Impossible to Represent\n\nGood TypeScript models don't just describe data, they prevent impossible combinations from existing in the first place.\n\nDiscriminated unions are one of the most effective ways to model these constraints.\n\n```ts\ntype State =\n  | { status: \"loading\" }\n  | { status: \"success\"; data: User }\n  | { status: \"error\"; error: Error };\n```\n\nThese models scale much better than loose optional property blobs because invalid states simply can't be represented.\n\nFuture refactors become safer because the compiler ensures every valid state is handled.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Use Exhaustive Checks With `never`\n\nOnce you've modeled your states as a discriminated union, exhaustiveness checking ensures every case is handled.\n\n```ts\ndefault: {\n  const exhaustive: never = state;\n  return exhaustive;\n}\n```\n\nAdd a new state, and the compiler immediately points out every place that needs updating.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Use `as const` for Configuration and Constants\n\nWithout `as const`:\n\n```ts\nconst theme = {\n  mode: \"dark\",\n};\n```\n\n`mode` becomes `string`.\n\nWith `as const`:\n\n```ts\nconst theme = {\n  mode: \"dark\",\n} as const;\n```\n\nNow it becomes `'dark'`.\n\nA small feature that dramatically improves inference for configuration objects and constants.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Use Type Predicates for Reusable Narrowing\n\nConnect runtime checks to compile-time intelligence.\n\n```ts\nfunction isUser(value: unknown): value is User {\n  return typeof value === \"object\" \u0026\u0026 value !== null \u0026\u0026 \"id\" in value;\n}\n```\n\nThen:\n\n```ts\nif (isUser(data)) {\n  data.id;\n}\n```\n\nThis becomes especially useful around APIs and external input boundaries.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Build New Types From Existing Types\n\nThink in transformations instead of duplication.\n\n```ts\ntype UserPreview = Pick\u003cUser, \"id\" | \"name\"\u003e;\n```\n\n### Learn these utility types\n\n- `Pick`\n- `Omit`\n- `Partial`\n- `Required`\n- Indexed access types\n\nThese utilities become much more valuable as applications grow.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Validate External Data at Runtime\n\nTypeScript does **not** validate API responses.\n\nThis is one of the most misunderstood parts of TypeScript.\n\n```ts\nconst UserSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n});\n```\n\nEvery API response, form submission, environment variable, JSON file, and user input is an untrusted boundary.\n\nTypeScript can't validate external data; you need runtime validation for that.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Avoid `enum` in Most Cases\n\nUsually simpler:\n\n```ts\nconst roles = [\"admin\", \"user\"] as const;\n```\n\nThan:\n\n```ts\nenum Role {\n  Admin,\n  User,\n}\n```\n\nIn most application code, literal unions are easier to refactor, serialize, and reason about than enums.\n\nEnums still have valid use cases, but they're often unnecessary.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Prefer Generics That Infer Automatically\n\nGreat TypeScript APIs rarely require manual generic arguments.\n\nLess ideal:\n\n```ts\ngetData\u003cUser\u003e();\n```\n\nBetter:\n\n```ts\ngetData(userSchema);\n```\n\nInference usually scales better than annotation-heavy APIs.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Turn On the Strict Compiler Options\n\nMany teams use TypeScript in \"autocomplete mode.\"\n\nStrict mode is where TypeScript really starts paying off.\n\n```json\n{\n  \"strict\": true,\n  \"useUnknownInCatchVariables\": true,\n  \"noUncheckedIndexedAccess\": true,\n  \"exactOptionalPropertyTypes\": true\n}\n```\n\nThese flags dramatically improve correctness.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### Learn Template Literal Types\n\nOne of the most powerful modern TypeScript features.\n\n```ts\ntype Route = `/api/${string}`;\n```\n\nExcellent for:\n\n- Routes\n- Event names\n- CSS utilities\n- Design systems\n- Query keys\n\nOnce you start using them, they show up everywhere.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n\n### \"Type-Safe\" Does Not Mean \"Runtime Safe\"\n\nA perfect final tip because it reframes everything.\n\nThis compiles:\n\n```ts\nconst user = (await response.json()) as User;\n```\n\nBut it may still fail at runtime.\n\nTypeScript improves correctness, but it isn't a runtime safety net.\n\n- It does not validate external data\n- It does not guarantee good architecture\n- It does not eliminate runtime bugs\n\nUse TypeScript to model your program well. Then validate anything that comes from the outside world.\n\n\u003csup\u003e[Table of Contents](#table-of-contents)\u003c/sup\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAllThingsSmitty%2Ftypescript-tips-everyone-should-know","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAllThingsSmitty%2Ftypescript-tips-everyone-should-know","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAllThingsSmitty%2Ftypescript-tips-everyone-should-know/lists"}