Skip to content

Commit 056e10a

Browse files
committed
Add typing migration examples to documentation
1 parent bda8c17 commit 056e10a

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

docs/typing.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,112 @@ Unknown keyword args and missing required args raise errors.
7272

7373
If a return type is annotated, the returned value is checked. If omitted, no return check is enforced.
7474

75+
## Migration examples
76+
77+
Use a boundary-first strategy: annotate entrypoints that receive external data, then tighten helpers and block callbacks.
78+
79+
### 1) Start with function boundaries
80+
81+
Before:
82+
83+
```vibe
84+
def calculate_total(items)
85+
items.reduce(0) do |acc, item|
86+
acc + item[:amount]
87+
end
88+
end
89+
```
90+
91+
After:
92+
93+
```vibe
94+
def calculate_total(items: array<{ amount: int }>) -> int
95+
items.reduce(0) do |acc: int, item: { amount: int }|
96+
acc + item[:amount]
97+
end
98+
end
99+
```
100+
101+
### 2) Migrate optional values with nullable or unions
102+
103+
Before:
104+
105+
```vibe
106+
def normalize_id(id)
107+
if id == nil
108+
"unknown"
109+
else
110+
id.string
111+
end
112+
end
113+
```
114+
115+
After:
116+
117+
```vibe
118+
def normalize_id(id: int | string | nil) -> string
119+
if id == nil
120+
"unknown"
121+
else
122+
id.string
123+
end
124+
end
125+
```
126+
127+
Use `T?` when the only optional case is `nil`, and use unions when multiple concrete kinds are allowed.
128+
129+
### 3) Convert loose hashes to shape contracts
130+
131+
Before:
132+
133+
```vibe
134+
def reward(payload)
135+
{ id: payload[:id], points: payload[:points] + 10 }
136+
end
137+
```
138+
139+
After:
140+
141+
```vibe
142+
def reward(payload: { id: string, points: int }) -> { id: string, points: int }
143+
{ id: payload[:id], points: payload[:points] + 10 }
144+
end
145+
```
146+
147+
Shapes are strict. Missing or extra keys fail checks.
148+
149+
### 4) Annotate block signatures where callbacks matter
150+
151+
Before:
152+
153+
```vibe
154+
def render_scores(scores)
155+
scores.map do |s|
156+
s + 1
157+
end
158+
end
159+
```
160+
161+
After:
162+
163+
```vibe
164+
def render_scores(scores: array<int>) -> array<int>
165+
scores.map do |s: int|
166+
s + 1
167+
end
168+
end
169+
```
170+
171+
Typed blocks catch callback mismatches at runtime with errors that include parameter name, expected type, and actual type.
172+
173+
### 5) Roll out incrementally
174+
175+
- Add annotations to one high-value path first.
176+
- Keep internal helpers untyped until boundary contracts stabilize.
177+
- Use `any` as a temporary bridge during migration.
178+
- Replace `any` with concrete or shape types once call sites are clean.
179+
- Watch runtime type errors in staging, then tighten signatures further.
180+
75181
## Time and Duration
76182

77183
Duration methods like `ago`/`after` return `Time`. Typed signatures use `time` or `time?` for those values.

0 commit comments

Comments
 (0)