Tips & Tricks (अपडेट: 2/6/2026)

Claude Code के साथ TypeScript Generics: keyof, constraints और API types

Claude Code से TypeScript Generics सीखें: constraints, keyof, mapped types, API result types और tsc verification।

Claude Code के साथ TypeScript Generics: keyof, constraints और API types

सिर्फ “generic बना दो” कहना काफी नहीं है

TypeScript Generics से एक ही function, type या class कई तरह के data shapes पर काम कर सकती है, और फिर भी input और output का संबंध सुरक्षित रहता है। शुरुआत में परेशानी T अक्षर से नहीं होती। असली समस्या तब आती है जब हम Claude Code से बस कहते हैं, “इसे generic बना दो”, लेकिन यह नहीं बताते कि कौन सा हिस्सा flexible रहना चाहिए और कौन सा हिस्सा constrained होना चाहिए। ऐसे prompt से अक्सर ऐसा code आता है जो reusable दिखता है, लेकिन अंदर से any, unknown या बहुत चौड़े Record<string, unknown> पर निर्भर होता है।

Production code में generics अक्सर API response, form, account, billing, analytics event और product CTA के पास होते हैं। यहां type कमजोर हुआ तो बात सिर्फ autocomplete की नहीं रहती; गलत value lead form, checkout या tracking event में जा सकती है। Masa का practical rule है: Claude Code से valid example और compile-time पर fail होने वाला example दोनों मांगो। अगर tsc गलत call को reject नहीं कर रहा, तो type contract अभी मजबूत नहीं है।

इस article का mental model:

input value -> T में type capture -> keyof T से key limit -> mapped types से shape transform -> tsc से contract verify

Syntax को TypeScript की official docs से check किया गया है: Generics, keyof Type Operator, Mapped Types, और Conditional Types। Claude Code workflow आगे बढ़ाने के लिए TypeScript tips और utility types guide भी पढ़ें।

Code से पहले review contract दें

Generics compile-time की चीज हैं। T runtime variable नहीं है; यह type parameter है जिससे compiler याद रखता है कि किस तरह का data function में आया और किस तरह का data वापस आना चाहिए। यहां extends का मतलब inheritance से ज्यादा “इस shape को satisfy करने वाले type ही allow करो” है। keyof T, T की property names का set बनाता है। mapped types उन्हीं property names पर चलकर नया type बनाते हैं।

Claude Code को पहले यह table दें:

सवालClaude Code को क्या बताना हैReview में क्या देखना है
T क्या हैdomain object, DTO या form modelresult original type नहीं खोता
क्या constrain होगाK extends keyof T, E extends ApiError, T extends objectinvalid call compile पर fail होती है
verification कैसे होगी@ts-expect-error, Expect, strict tsc commandbad example सच में fail होता है

यह table एक common गलती रोकता है: Claude Code code को compile कराने के लिए अंत में cast लगा देता है। Cast कभी-कभी ठीक है, जैसे Object.fromEntries के बाद TypeScript exact mapped type infer नहीं कर पाता। लेकिन cast कमजोर design छिपाने के लिए नहीं होना चाहिए।

Use case 1: safe key से duplicate हटाना

पहला example uniqueBy है। यह API rows, CSV import, admin table और UI list में काम आता है। अगर key को सिर्फ string लिख दिया जाए, तो caller ऐसी property भी दे सकता है जो object में है ही नहीं। K extends keyof T से key object की real properties तक सीमित रहती है।

type User = {
  id: string;
  email: string;
  role: "admin" | "editor";
  score: number;
};

function uniqueBy<T>(items: readonly T[]): T[];
function uniqueBy<T, K extends keyof T>(items: readonly T[], key: K): T[];
function uniqueBy<T, K extends keyof T>(items: readonly T[], key?: K): T[] {
  const seen = new Set<unknown>();
  const output: T[] = [];

  for (const item of items) {
    const value = key === undefined ? item : item[key];
    if (seen.has(value)) continue;
    seen.add(value);
    output.push(item);
  }

  return output;
}

const users: User[] = [
  { id: "u_1", email: "masa@example.com", role: "admin", score: 92 },
  { id: "u_2", email: "editor@example.com", role: "editor", score: 88 },
  { id: "u_1", email: "masa+copy@example.com", role: "admin", score: 70 },
];

const byId = uniqueBy(users, "id");
const byRole = uniqueBy(users, "role");

// @ts-expect-error "missing" is not a key of User.
uniqueBy(users, "missing");

console.log(byId.map((user) => user.id));
console.log(byRole.map((user) => user.role));

Prompt में साफ लिखें: “overloads use करो, key को keyof T तक limit करो, और missing key के लिए @ts-expect-error test जोड़ो।” ऐसा न करने पर Claude Code key: string और item[key as keyof T] जैसा code दे सकता है, जो risk को runtime तक धकेल देता है।

Use case 2: API response को optional soup न बनाएं

दूसरा example API response type है। कई codebase एक ही interface में data?: T और error?: ApiError लिख देते हैं। यह आसान लगता है, लेकिन caller को हर बार check करना पड़ता है कि success में data है या नहीं, failure में error है या नहीं। Discriminated union state को साफ बनाती है: success में data, failure में error, और ok से type narrow होता है।

type ApiError = {
  code: string;
  message: string;
  retryable: boolean;
};

type ApiResult<T, E extends ApiError = ApiError> =
  | { ok: true; status: number; data: T; error?: never }
  | { ok: false; status: number; error: E; data?: never };

type UserDto = {
  id: string;
  name: string;
  plan: "free" | "pro";
};

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}

function parseUserResponse(json: unknown): ApiResult<UserDto> {
  if (
    isRecord(json) &&
    typeof json.id === "string" &&
    typeof json.name === "string" &&
    (json.plan === "free" || json.plan === "pro")
  ) {
    return {
      ok: true,
      status: 200,
      data: { id: json.id, name: json.name, plan: json.plan },
    };
  }

  return {
    ok: false,
    status: 422,
    error: {
      code: "INVALID_USER_RESPONSE",
      message: "User response does not match the expected shape.",
      retryable: false,
    },
  };
}

function unwrap<T, E extends ApiError>(result: ApiResult<T, E>): T {
  if (result.ok) {
    return result.data;
  }

  throw new Error(`${result.error.code}: ${result.error.message}`);
}

const parsed = parseUserResponse({ id: "u_1", name: "Masa", plan: "pro" });
const user = unwrap(parsed);
console.log(user.name.toUpperCase());

यह pattern Claude Code के लिए अच्छा है, क्योंकि prompt में runtime validation और type contract दोनों बताए जा सकते हैं। API design के लिए Claude Code API development और testing के लिए API testing guide देखें।

Use case 3: mapped types से form state बनाना

तीसरा example form state है। Business model से हर field के लिए value, dirty, errors वाला state बनाते हैं। mapped types से field names दोबारा नहीं लिखने पड़ते, और value type सुरक्षित रहता है: email string, seats number, newsletter boolean।

type FieldState<T> = {
  value: T;
  dirty: boolean;
  errors: string[];
};

type FormState<T extends object> = {
  [K in keyof T]: FieldState<T[K]>;
};

function createFormState<T extends object>(initial: T): FormState<T> {
  const entries = Object.entries(initial).map(([key, value]) => [
    key,
    { value, dirty: false, errors: [] },
  ]);

  return Object.fromEntries(entries) as FormState<T>;
}

function setField<T extends object, K extends keyof T>(
  state: FormState<T>,
  key: K,
  value: T[K],
): FormState<T> {
  return {
    ...state,
    [key]: { value, dirty: true, errors: [] },
  } as FormState<T>;
}

type SignupForm = {
  email: string;
  seats: number;
  newsletter: boolean;
};

const form = createFormState<SignupForm>({
  email: "team@example.com",
  seats: 2,
  newsletter: true,
});

const updated = setField(form, "seats", 3);

// @ts-expect-error seats must be a number.
setField(form, "seats", "three");

console.log(updated.seats.value);

यहां Object.fromEntries के बाद cast review point है। यह external input पर भरोसा नहीं कर रहा; यह बताता है कि transformation same keys रखता है, लेकिन TypeScript exact mapped type infer नहीं कर पाता। Claude Code से हर cast का reason लिखवाएं।

tsc और type tests से verify करें

Generics example सिर्फ पढ़ने के लिए नहीं होना चाहिए। Code को examples/generics.ts में रखकर strict compiler check चलाएं।

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noEmit": true,
    "lib": ["ES2022", "DOM"]
  },
  "include": ["examples/**/*.ts"]
}
npm install --save-dev typescript
npx tsc --noEmit --strict --lib ES2022,DOM examples/generics.ts

Compile-time type assertion के लिए यह helper काम आता है। यह runtime पर कुछ नहीं करता, लेकिन expected type बदलने पर build fail हो जाती है।

type Equal<A, B> =
  (<T>() => T extends A ? 1 : 2) extends
  (<T>() => T extends B ? 1 : 2)
    ? true
    : false;

type Expect<T extends true> = T;

type PickReadonly<T, K extends keyof T> = {
  readonly [P in K]: T[P];
};

type Account = {
  id: string;
  email: string;
  seats: number;
};

type PublicAccount = PickReadonly<Account, "id" | "email">;

type PublicAccountCheck = Expect<
  Equal<PublicAccount, { readonly id: string; readonly email: string }>
>;

const leaked: PublicAccount = {
  id: "a_1",
  email: "team@example.com",
  // @ts-expect-error seats is intentionally not part of PublicAccount.
  seats: 10,
};

console.log("Type checks are compile-time only.");

Claude Code type review templates

Code generate करने के बाद Claude Code से type review भी कराएं।

Template 1: generic helper review
इस TypeScript function को review करें।
Goal: selected key से input array के duplicates हटाना।
Constraints: key must be K extends keyof T. any forbidden. missing key के लिए @ts-expect-error शामिल करें।
Output: issues, corrected code, और verify करने के लिए tsc command।
Template 2: API response type review
इस API response type को review करें।
Goal: success में data, failure में error।
Constraints: data?: T जैसे vague optional fields से बचें। ok से type narrowing confirm करें।
Output: safe caller example, failure example, extra type tests।
Template 3: mapped types review
इस mapped type को review करें।
Goal: existing form model से field state type बनाना।
Constraints: keyof, T[K], readonly, optional properties और necessary cast explain करें।
Output: type flow, fragile cases, minimal safe fix।
Template 4: pre-PR type audit
इस diff में generics, conditional types और mapped types audit करें।
Check: any, too broad Record, unnecessary type parameters, missing @ts-expect-error, missing runtime validation।
Output: blockers, minor improvements, extra tests priority order में।

Common pitfalls

Pitfallक्या टूटता हैसुरक्षित आदत
any से generic दिखानाreturn type की जानकारी खो जाती हैrelation को T से capture करें
key को string लिखनाmissing property compile हो जाती हैK extends keyof T use करें
Record<string, unknown> का overusespecific properties गायब हो जाती हैंdictionary न हो तो object सोचें
API fields सब optionalcaller data या error पर भरोसा नहीं कर सकताdiscriminated union use करें
unexplained castsreviewer safety नहीं समझताcast से पहले invariant लिखें

T extends object और T extends Record<string, unknown> का फर्क ध्यान रखें। Form model को अक्सर सिर्फ object होना चाहिए। Arbitrary string keys वाले dictionary helper में Record सही हो सकता है।

CTA: type safety को revenue path से जोड़ें

Generics केवल syntax exercise नहीं है। Form, checkout, API payload, product template या analytics event का type कमजोर हुआ तो reader से customer तक का path टूट सकता है। Daily commands के लिए free Claude Code cheatsheet से शुरुआत करें। Reusable prompts और setup material चाहिए तो ClaudeCodeLab products देखें। Team को CLAUDE.md, type review rules, CI और rollout standardize करना हो तो Claude Code training बेहतर next step है।

अपने repository में पहले उन types को देखें जो business के करीब हैं: account, billing, form, API response और tracking. Claude Code से सिर्फ “क्या यह compile होता है?” न पूछें; यह भी पूछें कि “क्या यह type mistake conversion path तोड़ सकती है?”

असल में आजमाने का परिणाम

इस workflow को आजमाने पर Masa ने पाया कि implementation prompt और type review prompt अलग रखने से result अधिक stable होता है। पहले helper generate कराएं, फिर Claude Code से any, missing keyof, overly optional API result और missing @ts-expect-error audit कराएं। uniqueBy और form state examples खास उपयोगी रहे, क्योंकि tsc --noEmit --strict valid calls को pass और intentionally invalid calls को reject करके contract के दोनों side दिखा देता है।

#Claude Code #TypeScript #generics #type safety #design patterns
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.