Deck 3 - TS Type Safety + React Typing Flashcards

TypeScript soundness + React typing: unknown/narrowing, unions, generics, props/state/refs/events. (80 cards)

1
Q

Deck 3 - TypeScript Type Safety + React Typing (objective)

A

Goal: write and review TS types like an evaluator.
You practice: sound types (unknown + narrowing), unions, generics, and React props/state/event typing.
Output style: 1 verdict sentence + 2-3 issues + safest fix.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Any vs unknown (define + why).

A
  • any: opts out of type safety (unsafe).
  • unknown: requires narrowing before use (safe).
  • Rule: prefer unknown for external data, then validate/narrow.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What is narrowing?

A
  • Refining a union type to a specific member via checks.
  • Tools: typeof, in, instanceof, Array.isArray, custom type guards, discriminants.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Define a user-defined type guard.

A
  • A function returning ‘x is T’ that checks runtime shape.
  • Enables safe narrowing of unknown/unions.
  • Must actually check at runtime (not just cast).
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Discriminated union (define).

A
  • Union of object types sharing a literal tag field (e.g., kind/status/type).
  • Switch on tag => safe access + exhaustiveness.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Exhaustive checks with never (why).

A
  • Ensure all union variants handled.
  • Pattern: default: assertNever(x).
  • New variants force compile errors.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Interface vs type (practical rule).

A
  • Both work for object shapes.
  • interface: extend/merge; type: unions/intersections/mapped/conditional.
  • Use what reads clearest.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Optional props and undefined (gotcha).

A
  • prop?: T means prop can be missing OR undefined.
  • If code assumes presence, guard or provide default.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Readonly and immutability.

A
  • readonly prevents mutation at compile time.
  • Prefer immutable updates for React state.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Literal types and ‘as const’.

A
  • ‘as const’ freezes values to literals and readonly.
  • Useful for discriminants and constant maps.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

satisfies operator (why).

A
  • Checks expression conforms to a type without widening.
  • Keeps literals while enforcing shape.
  • Often better than ‘as SomeType’.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Generics (define).

A
  • Parameterize types/functions/components.
  • Use constraints (extends) to require fields.
  • Prefer inference; annotate only when needed.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

keyof / typeof / indexed access (define).

A
  • typeof gets type of a value.
  • keyof gets keys of a type.
  • T[K] gives property type safely.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Utility types (common).

A
  • Partial, Required, Pick, Omit, Record, ReturnType, Parameters.
  • Use to avoid duplicated shapes.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Function overloads (when).

A
  • Different call signatures with one implementation.
  • Use when return type depends on input shape.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Non-null assertion (!) (review rule).

A
  • ’!’ silences null/undefined checks.
  • Avoid unless proven by invariant.
  • Prefer real guards.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Type assertions ‘as T’ (review rule).

A
  • Assertion does not validate runtime shape.
  • Avoid for external data.
  • Prefer unknown + narrowing/guards.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

React children typing (baseline).

A
  • children is usually React.ReactNode.
  • Return type is typically JSX.Element | null.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

React event typing (common).

A
  • onChange: React.ChangeEvent<HTMLInputElement></HTMLInputElement>
  • onClick: React.MouseEvent<HTMLButtonElement></HTMLButtonElement>
  • Prefer currentTarget over target.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Typing hooks (baseline).

A
  • useState: include union with null when needed.
  • useRef: useRef<T | null>(null).
  • Avoid any in state/ref.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Gotcha: external JSON typed as any.

const data = await res.json();
// data is any
console.log(data.user.name.toUpperCase());
A

Verdict: FAIL
- Issue: res.json() is effectively any; no validation.
- Impact: runtime crash if shape differs.
- Fix: json -> unknown, then narrow/validate.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Gotcha: union not narrowed.

type ApiOk = { ok: true; user: { id: string; name: string } };
type ApiErr = { ok: false; error: string };
function f(res: ApiOk | ApiErr) {
  return res.user.name;
}
A

Verdict: FAIL
- Issue: res.user accessed without narrowing.
- Fix: if (res.ok) { … } else { … }.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Gotcha: using ‘as’ instead of narrowing.

const user = data as { id: string; name: string };
setName(user.name.toUpperCase());
A

Verdict: FAIL
- Issue: assertion does not validate.
- Fix: validate unknown with a guard (or schema) before use.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Gotcha: optional prop assumed present.

type Props = { userId?: string };
function Profile({ userId }: Props) {
  return <div>{userId.toUpperCase()}</div>;
}
A

Verdict: FAIL
- Issue: userId can be undefined.
- Fix: make required or guard/default/conditional render.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Gotcha: state typed as any. ``` const [user, setUser] = useState(null); user.name.toUpperCase(); ```
Verdict: FAIL - Issue: any removes safety. - Fix: useState(null) + guard user.
26
Gotcha: ref missing null. ``` const inputRef = useRef(null); inputRef.current.focus(); ```
Verdict: FAIL - Issue: ref starts null. - Fix: useRef(null) + guard current.
27
Gotcha: non-null assertion without invariant. ``` inputRef.current!.focus(); ```
Verdict: FAIL - Issue: '!' hides potential null. - Fix: guard current or enforce invariant.
28
Gotcha: string index can be undefined. ``` type Map = { [k: string]: number }; const m: Map = { a: 1 }; const v = m['missing'].toFixed(2); ```
Verdict: FAIL - Issue: missing key yields undefined at runtime. - Fix: model as number | undefined and guard, or check key existence.
29
Gotcha: string key too wide. ``` const routes = { home: '/', about: '/about' }; type Route = keyof typeof routes; function go(r: string) { return routes[r]; } ```
Verdict: FAIL - Issue: r is string, not Route. - Fix: r: Route or validate string to Route.
30
Gotcha: missing key in Record. ``` type Status = 'idle' | 'loading' | 'error'; const labels: Record = { idle: 'Idle', loading: 'Loading' }; ```
Verdict: FAIL - Issue: 'error' missing. - Fix: include key; 'satisfies Record' also works well.
31
Gotcha: missing exhaustive check. ``` type S = { kind: 'a'; a: number } | { kind: 'b'; b: number }; function read(s: S) { switch (s.kind) { case 'a': return s.a; case 'b': return s.b; } } ```
Verdict: PARTIAL FAIL - Issue: no default/exhaustive; may return undefined. - Fix: add default assertNever(s).
32
Gotcha: assertNever helper (pattern). ``` function assertNever(x: never): never { throw new Error('unreachable'); } ```
Verdict: PASS - Use in switch default to enforce exhaustiveness.
33
Gotcha: tuple widened to union array. ``` const pair: [string, number] = ['a', 1]; const xs: (string | number)[] = pair; xs[1].toFixed(2); ```
Verdict: FAIL - Issue: xs[1] is string | number. - Fix: keep tuple type or narrow before calling number methods.
34
Gotcha: optional chaining still unsafe. ``` props.user?.name.toUpperCase(); ```
Verdict: FAIL - Issue: name can be undefined. - Fix: props.user?.name?.toUpperCase() or guard.
35
Gotcha: event typed as any. ``` function onChange(e: any) { console.log(e.target.value); } ```
Verdict: FAIL - Issue: any removes safety. - Fix: React.ChangeEvent + e.currentTarget.value.
36
Gotcha: target vs currentTarget. ``` function onClick(e: React.MouseEvent) { console.log(e.target.disabled); } ```
Verdict: FAIL - Issue: target can be child. - Fix: e.currentTarget.disabled.
37
Gotcha: generic missing constraint. ``` function getId(x: T) { return x.id; } ```
Verdict: FAIL - Issue: id may not exist. - Fix: T extends { id: string }.
38
Gotcha: lying type guard. ``` function isUser(x: unknown): x is { id: string; name: string } { return true; } ```
Verdict: FAIL - Issue: guard must validate runtime shape. - Fix: check object + keys + types.
39
Gotcha: null/undefined mismatch in state. ``` type User = { name: string }; const [u, setU] = useState(null as any); ```
Verdict: FAIL - Issue: type says undefined but value uses null. - Fix: choose User | null OR User | undefined consistently.
40
Gotcha: get with string key. ``` type User = { id: string; name: string }; function get(u: User, k: string) { return u[k]; } ```
Verdict: FAIL - Issue: k should be keyof User. - Fix: k: keyof User; return User[keyof User].
41
Gotcha: Partial then treated as complete. ``` type User = { id: string; name: string }; const [u, setU] = useState>({}); console.log(u.name.toUpperCase()); ```
Verdict: FAIL - Issue: Partial means fields may be missing. - Fix: use User | null or validate required fields.
42
Gotcha: children typed too narrowly. ``` type Props = { children: JSX.Element }; function Wrap({ children }: Props) { return
{children}
; } ```
Verdict: FAIL - Issue: children can be string/array/fragment. - Fix: React.ReactNode.
43
Gotcha: json() return not typed, becomes any. ``` async function load() { return fetch(url).then(r => r.json()); } const x = await load(); x.notARealField; ```
Verdict: FAIL - Issue: json is any; x is any. - Fix: return unknown; validate to T.
44
Gotcha: parse JSON returns any. ``` function parse(x: string): any { return JSON.parse(x); } ```
Verdict: FAIL - Issue: any hides contract. - Fix: return unknown and narrow/validate.
45
Gotcha: counts object implicit any index. ``` const counts = {}; counts[1] = 2; ```
Verdict: FAIL - Issue: implicit any indexing. - Fix: Record or Map.
46
Gotcha: unsafe 'in' check without object guard. ``` function hasName(x: unknown) { return 'name' in (x as any); } ```
Verdict: FAIL - Issue: unsafe cast; 'in' requires non-null object. - Fix: typeof x === 'object' && x !== null && 'name' in x.
47
Gotcha: unsafe satisfies vs as. ``` type Cfg = { mode: 'dev' | 'prod' }; const cfg = { mode: 'dev', extra: 1 } as Cfg; ```
Verdict: FAIL - Issue: 'as' bypasses checks. - Fix: use satisfies: const cfg = { ... } satisfies Cfg.
48
Gotcha: action reducer not exhaustive. ``` type Action = { type: 'inc' } | { type: 'dec' }; function reduce(a: Action) { if (a.type === 'inc') return 1; return 0; } ```
Verdict: PARTIAL FAIL - Issue: dec not handled explicitly. - Fix: switch + assertNever for exhaustiveness.
49
Gotcha: Date typed as Date from JSON. ``` type Api = { createdAt: Date }; const data = (await res.json()) as Api; data.createdAt.getTime(); ```
Verdict: FAIL - Issue: JSON gives string, not Date. - Fix: validate string then convert to Date.
50
Gotcha: querySelector cast without guard. ``` const el = document.querySelector('#x') as HTMLInputElement; el.value; ```
Verdict: FAIL - Issue: may be null or wrong element type. - Fix: guard: el instanceof HTMLInputElement.
51
Gotcha: readonly defeated by cast. ``` const xs: readonly number[] = [1,2]; (xs as number[]).push(3); ```
Verdict: FAIL - Issue: cast defeats readonly. - Fix: do not mutate; create new array.
52
Gotcha: useState literal union needs explicit type. ``` const [status, setStatus] = useState('idle'); setStatus('loading'); ```
Verdict: PARTIAL - Issue: status can widen to string. - Fix: useState<'idle'|'loading'>('idle').
53
Gotcha: generic accessor correct pattern. ``` function pluck(obj: T, key: K) { return obj[key]; } ```
Verdict: PASS - Correct constraint for safe indexed access.
54
Gotcha: Omit then cast around it. ``` type User = { id: string; name: string; email: string }; type PublicUser = Omit; const u: PublicUser = { id: '1', name: 'a', email: 'x' as any }; ```
Verdict: FAIL - Issue: cast defeats Omit. - Fix: remove email; do not cast to bypass.
55
Gotcha: optional chaining nested field still unsafe. ``` type U = { profile?: { name?: string } }; function up(u: U) { return u.profile?.name.toUpperCase(); } ```
Verdict: FAIL - Issue: name can be undefined. - Fix: u.profile?.name?.toUpperCase() or guard.
56
Gotcha: widening loses literal union. ``` const status = 'idle'; function set(s: 'idle' | 'loading') {} set(status); ```
Verdict: FAIL - Issue: status can widen to string. - Fix: annotate or use as const.
57
Gotcha: overload may be needed. ``` function head(x: string | string[]) { return x[0]; } ```
Verdict: PARTIAL - Issue: return type depends on input. - Fix: overloads or narrow input.
58
Gotcha: optional prop + toFixed. ``` type Props = { size?: number }; function Box({ size }: Props) { return
{size.toFixed(0)}
; } ```
Verdict: FAIL - Issue: size can be undefined. - Fix: default value or guard.
59
Gotcha: optional prop but component cannot function. ``` type Props = { id?: string }; function Load({ id }: Props) { fetch(`/api/${id}`); return null; } ```
Verdict: FAIL - Issue: id should be required. - Fix: make id required or guard + return early.
60
Gotcha: union not narrowed before method. ``` function len(x: string | string[]) { return x.length.toFixed(0); } ```
Verdict: FAIL - Issue: length is number; toFixed ok, but x could be string/array; logic unclear. - Fix: narrow to express correct intent; avoid misleading union usage.
61
Scenario: verdict + 3 issues. ``` type Props = { userId?: string }; function Profile({ userId }: Props) { return
{userId.toUpperCase()}
; } ```
Verdict: FAIL 1) userId optional -> undefined 2) missing guard/default 3) should be required if component cannot function without it
62
Scenario: fix approach for external JSON (no libs).
Treat json as unknown; validate with real guard; avoid 'as T' assertions; only then set state/use fields.
63
Scenario: discriminated union narrowing (pattern).
Branch on shared literal field (ok/kind/type). Ok branch has success payload; else branch has error.
64
Scenario: exhaustive switch pattern (template).
Switch on discriminant; add default: return assertNever(x) so new variants force updates.
65
Scenario: what must a type guard do?
Check runtime: typeof, non-null object, required keys, key types; only then return x is T.
66
Scenario: React children typing rule.
Use children: React.ReactNode by default; do not require JSX.Element unless needed.
67
Scenario: ref typing for input.
useRef(null); guard ref.current before calling methods.
68
Scenario: safe generic accessor signature.
pluck(obj: T, key: K): T[K].
69
Scenario: why down-rate non-null assertion?
It hides null risks; evaluators prefer explicit guards or proven invariants.
70
Scenario: satisfies vs as (recommendation).
Use satisfies to enforce shape while keeping literals; avoid 'as' when it bypasses checks.
71
Scenario: Date fields from JSON.
Model API as string; validate string; convert to Date intentionally; do not type JSON Date as Date.
72
Scenario: safe 'in' usage on unknown.
First check typeof x === 'object' && x !== null; then use 'in' or key access.
73
Scenario: primary category for any in state.
Correctness/Safety: any bypasses checks and can hide runtime failures.
74
Scenario: overloads vs unions.
Use overloads when return type depends on specific input shape; otherwise union + narrowing is enough.
75
Scenario: evaluator one-liner for unsafe assertion.
FAIL - The code asserts an unvalidated shape with 'as', bypassing type safety; use unknown + runtime validation/narrowing.
76
Checklist: TS evaluator top 5.
1) Avoid any (use unknown + narrowing) 2) Validate external data 3) Prefer discriminated unions 4) Avoid unsafe assertions/! 5) Type React props/state/ref/events accurately
77
Checklist: narrowing toolkit.
typeof / instanceof / in Array.isArray literal discriminants custom type guards exhaustive checks with never
78
Checklist: React typing quick pass.
- Required vs optional props - children: ReactNode - Events: ChangeEvent/MouseEvent - State includes null when needed - Refs include null + guards
79
Checklist: discriminated union best practice.
- Shared literal tag field - Branch on tag to narrow - Exhaustive check to prevent silent fallthrough
80
Checklist: safe external JSON handling.
json -> unknown validate keys + types convert (Date/number) intentionally only then store/use in UI