sorbet_typed-props 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.cz.yaml +1 -1
- data/.prettierignore +1 -0
- data/CHANGELOG.md +12 -0
- data/README.md +103 -26
- data/docs/defaults.md +198 -0
- data/docs/inheritance.md +139 -0
- data/docs/internal-props.md +283 -0
- data/docs/method-visibility.md +112 -0
- data/docs/phlex.md +98 -0
- data/docs/post-init-hook.md +131 -0
- data/docs/props-and-const.md +170 -0
- data/lib/sorbet_typed/props/version.rb +1 -1
- data/lib/sorbet_typed/props/without_init_param_and_no_default_plugin.rb +9 -3
- data/tested_gem_versions.yml +37 -5
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38708cf1f39a8cef314d4843c4fed453f4faf8f2fc35db4e47e1c659dcd581d7
|
|
4
|
+
data.tar.gz: 94b1bf4f088610d3e3e3bb1eb1ea7f09da8428f9849176d214f4f0a9d2760ff7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d37eeeaea430f1950b0a9bc80e50e5c98945458082ee037989c213151a593e67ecf9e52816b05c371636b8110b01d161ba53418f601ec514d654c5ea4abfa374
|
|
7
|
+
data.tar.gz: e54af5f419e3cea6b5aaacc4379db254d17c3cc6da84be35c8ab715149194466a1c5cec687896dc2cf93027541f1a5af88246ac4fd83d99fa92d3d54801e973e
|
data/.cz.yaml
CHANGED
data/.prettierignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.superpowers/
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## v1.5.2 (2026-07-03)
|
|
2
|
+
|
|
3
|
+
### Fix
|
|
4
|
+
|
|
5
|
+
- **renovate**: stop creating MRs before minimumReleaseAge elapses
|
|
6
|
+
|
|
7
|
+
## v1.5.1 (2026-06-23)
|
|
8
|
+
|
|
9
|
+
### Fix
|
|
10
|
+
|
|
11
|
+
- **props**: adapt to sorbet-runtime bound_setter signature change
|
|
12
|
+
|
|
1
13
|
## v1.5.0 (2026-06-22)
|
|
2
14
|
|
|
3
15
|
### Feat
|
data/README.md
CHANGED
|
@@ -14,10 +14,13 @@ the
|
|
|
14
14
|
which doesn't really work with sorbet, if you want to have everything fully typed (and not use two runtime type-systems
|
|
15
15
|
in parallel).
|
|
16
16
|
|
|
17
|
+
- [Tested with](#tested-with)
|
|
17
18
|
- [Installation](#installation)
|
|
18
19
|
- [Usage](#usage)
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
20
|
+
- [Defaults and optional props](#defaults-and-optional-props)
|
|
21
|
+
- [Internal props](#internal-props)
|
|
22
|
+
- [Inheritance](#inheritance)
|
|
23
|
+
- [Phlex integration](#phlex-integration)
|
|
21
24
|
- [Extending initializer logic](#extending-initializer-logic)
|
|
22
25
|
- [Method Visibility](#method-visibility)
|
|
23
26
|
- [Development](#development)
|
|
@@ -27,12 +30,27 @@ in parallel).
|
|
|
27
30
|
|
|
28
31
|
<!-- begin:tested-versions -->
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
| sorbet
|
|
33
|
+
This gem is tested against all combinations of the following dependency versions.
|
|
34
|
+
|
|
35
|
+
| | Tested versions |
|
|
36
|
+
| -------------- | -------------------- |
|
|
37
|
+
| Ruby | 3.3, 3.4, 4.0 |
|
|
38
|
+
| sorbet-runtime | 0.6.12997, 0.6.13321 |
|
|
39
|
+
| tapioca | 0.17, 0.18, 0.19 |
|
|
40
|
+
| sorbet | 0.6.12997, 0.6.13321 |
|
|
41
|
+
|
|
42
|
+
### Excluded combinations
|
|
43
|
+
|
|
44
|
+
Some version combinations are incompatible with each other independently of this gem — users depending on those
|
|
45
|
+
combinations would face the same conflict in their own projects. These are excluded from the test matrix:
|
|
46
|
+
|
|
47
|
+
- `tapioca < 0.19.0` + `sorbet-runtime >= 0.6.13149` — tapioca < 0.19 calls has_rest on T::Private::Methods::Signature
|
|
48
|
+
which was removed in sorbet-runtime 0.6.13149
|
|
49
|
+
- `tapioca < 0.19.1` + `sorbet-runtime < 0.6.12698` — tapioca < 0.19.1 references T::Module which was added to
|
|
50
|
+
sorbet-runtime in 0.6.12698
|
|
51
|
+
- `tapioca >= 0.19.1` + `sorbet-runtime < 0.6.12698` — tapioca >= 0.19.1 enforces sorbet-runtime >= 0.6.12698 in its
|
|
52
|
+
gemspec; listed explicitly so the test generator skips sorbet-runtime < 0.6.12698 rather than treating the bundler
|
|
53
|
+
rejection as an unexpected error
|
|
36
54
|
|
|
37
55
|
<!-- end:tested-versions -->
|
|
38
56
|
|
|
@@ -67,43 +85,99 @@ my_object.my_prop # => "foo"
|
|
|
67
85
|
my_object.my_prop = 'bar'
|
|
68
86
|
```
|
|
69
87
|
|
|
70
|
-
|
|
88
|
+
→ [Full documentation](docs/props-and-const.md)
|
|
89
|
+
|
|
90
|
+
### Defaults and optional props
|
|
91
|
+
|
|
92
|
+
Use `default:` for a static fallback value or `factory:` for a freshly constructed default per instance:
|
|
71
93
|
|
|
72
94
|
```ruby
|
|
73
|
-
class
|
|
74
|
-
extend T::Sig
|
|
95
|
+
class Config
|
|
75
96
|
include SorbetTyped::Props
|
|
76
97
|
|
|
77
|
-
|
|
98
|
+
const :timeout_seconds, Integer, default: 30
|
|
99
|
+
const :logger, Logger, factory: -> { Logger.new($stdout) }
|
|
100
|
+
end
|
|
78
101
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
102
|
+
Config.new # uses defaults
|
|
103
|
+
Config.new(timeout_seconds: 60) # override specific prop
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
→ [Full documentation](docs/defaults.md)
|
|
107
|
+
|
|
108
|
+
### Internal props
|
|
109
|
+
|
|
110
|
+
Use `extra: { initializer: false }` for attributes that are set after construction, not at it — avoiding `T.must` noise
|
|
111
|
+
at read sites for order-dependent implementations:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
class Pipeline
|
|
115
|
+
include SorbetTyped::Props
|
|
116
|
+
|
|
117
|
+
const :input, String
|
|
118
|
+
prop :result, String, extra: { initializer: false }
|
|
119
|
+
|
|
120
|
+
def run
|
|
121
|
+
self.result = process(input)
|
|
82
122
|
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def process(value) = value.upcase
|
|
83
127
|
end
|
|
128
|
+
|
|
129
|
+
pipeline = Pipeline.new(input: 'hello')
|
|
130
|
+
pipeline.result # => TypeError("Property `result` of `Pipeline` was read before being set")
|
|
131
|
+
pipeline.run
|
|
132
|
+
T.reveal_type(pipeline.result) # => String
|
|
133
|
+
pipeline.result # => 'HELLO'
|
|
84
134
|
```
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
→ [Full documentation](docs/internal-props.md)
|
|
87
137
|
|
|
88
|
-
|
|
138
|
+
### Inheritance
|
|
139
|
+
|
|
140
|
+
Props are inherited by subclasses. The Tapioca compiler generates a combined `initialize` signature for each concrete
|
|
141
|
+
class:
|
|
89
142
|
|
|
90
143
|
```ruby
|
|
91
|
-
class
|
|
92
|
-
extend T::Sig
|
|
144
|
+
class Animal
|
|
93
145
|
include SorbetTyped::Props
|
|
94
146
|
|
|
95
|
-
prop :
|
|
147
|
+
prop :name, String
|
|
148
|
+
end
|
|
96
149
|
|
|
97
|
-
|
|
150
|
+
class Dog < Animal
|
|
151
|
+
prop :breed, String
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
Dog.new(name: 'Rex', breed: 'Labrador')
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
→ [Full documentation](docs/inheritance.md)
|
|
158
|
+
|
|
159
|
+
### Phlex integration
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
class Components::HelloWorld < Phlex::HTML
|
|
163
|
+
extend T::Sig
|
|
164
|
+
include SorbetTyped::Props
|
|
165
|
+
|
|
166
|
+
prop :name, String
|
|
98
167
|
|
|
99
168
|
sig { void }
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
T.reveal_type(@my_var) # => T.nilable(String)
|
|
169
|
+
def view_template
|
|
170
|
+
h1 { "Hello, #{name}!" }
|
|
103
171
|
end
|
|
104
172
|
end
|
|
105
173
|
```
|
|
106
174
|
|
|
175
|
+
> **Known limitation:** Phlex overrides `::new` rather than `#initialize` in `Phlex::SGML`, so Sorbet cannot statically
|
|
176
|
+
> validate props on Phlex components. Calling `HelloWorld.new` without required props will not raise a Sorbet error. See
|
|
177
|
+
> issue [#23](https://gitlab.com/sorbet_typed/props/-/work_items/23).
|
|
178
|
+
|
|
179
|
+
→ [Full documentation](docs/phlex.md)
|
|
180
|
+
|
|
107
181
|
### Extending initializer logic
|
|
108
182
|
|
|
109
183
|
If you want to implement some logic on instance initialization, implementing a custom `initializer` method would require
|
|
@@ -128,8 +202,9 @@ class MyClass
|
|
|
128
202
|
end
|
|
129
203
|
```
|
|
130
204
|
|
|
131
|
-
|
|
132
|
-
|
|
205
|
+
→ [Full documentation](docs/post-init-hook.md)
|
|
206
|
+
|
|
207
|
+
**Note:** you cannot do instance variable initialization here.
|
|
133
208
|
|
|
134
209
|
### Method Visibility
|
|
135
210
|
|
|
@@ -186,6 +261,8 @@ my_object.baz
|
|
|
186
261
|
my_object.bar # => "baz"
|
|
187
262
|
```
|
|
188
263
|
|
|
264
|
+
→ [Full documentation](docs/method-visibility.md)
|
|
265
|
+
|
|
189
266
|
Putting prop-definition after an access modifier without arguments does not work:
|
|
190
267
|
|
|
191
268
|
```ruby
|
data/docs/defaults.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Defaults and optional props
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Table of Contents](#table-of-contents)
|
|
6
|
+
- [What they do](#what-they-do)
|
|
7
|
+
- [When to use](#when-to-use)
|
|
8
|
+
- [Options](#options)
|
|
9
|
+
- [Examples](#examples)
|
|
10
|
+
- [Prop with a static default](#prop-with-a-static-default)
|
|
11
|
+
- [Const with a static default](#const-with-a-static-default)
|
|
12
|
+
- [Nilable prop without default — still required (statically)](#nilable-prop-without-default--still-required-statically)
|
|
13
|
+
- [Nilable prop with default nil — optional](#nilable-prop-with-default-nil--optional)
|
|
14
|
+
- [Const with factory — dependency injection](#const-with-factory--dependency-injection)
|
|
15
|
+
- [Behavior notes](#behavior-notes)
|
|
16
|
+
|
|
17
|
+
## What they do
|
|
18
|
+
|
|
19
|
+
`default:` and `factory:` are native [Sorbet `T::Props`](https://sorbet.org/docs/tstruct) options. They make a prop or
|
|
20
|
+
const optional in `initialize`. When the caller does not supply the argument, the declared default or factory result is
|
|
21
|
+
used instead.
|
|
22
|
+
|
|
23
|
+
## When to use
|
|
24
|
+
|
|
25
|
+
- A prop has a sensible fallback value that most callers will not need to override.
|
|
26
|
+
- You want to inject a collaborator (e.g. a repository or service object) with a real-world default, while keeping the
|
|
27
|
+
ability to swap it out in tests.
|
|
28
|
+
|
|
29
|
+
## Options
|
|
30
|
+
|
|
31
|
+
| Option | Value type | Description |
|
|
32
|
+
| ------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `default: value` | Any value matching prop type | Static default. The same object is used across all instances. Use only for immutable values (numbers, Symbols). |
|
|
34
|
+
| `factory: -> { … }` | `Proc` returning the prop type | Called once per instantiation. Use for mutable defaults or objects that must be freshly constructed per instance. |
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Prop with a static default
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
class Counter
|
|
42
|
+
include SorbetTyped::Props
|
|
43
|
+
|
|
44
|
+
prop :count, Integer
|
|
45
|
+
prop :step, Integer, default: 1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Counter.new(count: 0) # step defaults to 1
|
|
49
|
+
Counter.new(count: 0, step: 5) # step overridden
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Generated `initialize` signature — `count` required, `step` optional:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
# sorbet/rbi/dsl/counter.rbi
|
|
56
|
+
# typed: true
|
|
57
|
+
|
|
58
|
+
class Counter
|
|
59
|
+
# Generated from sorbet SorbetTyped::Props
|
|
60
|
+
sig { params(count: ::Integer, step: ::Integer).void }
|
|
61
|
+
def initialize(count:, step: T.unsafe(nil)); end
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Const with a static default
|
|
66
|
+
|
|
67
|
+
`const` with `default:` is optional in `initialize` but has no writer — the value is fixed after construction.
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
class Config
|
|
71
|
+
include SorbetTyped::Props
|
|
72
|
+
|
|
73
|
+
const :timeout_seconds, Integer, default: 30
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
Config.new # timeout_seconds == 30
|
|
77
|
+
Config.new(timeout_seconds: 60) # timeout_seconds == 60
|
|
78
|
+
# Config.new.timeout_seconds = 10 # NoMethodError — const has no writer
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Generated `initialize` signature:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
# sorbet/rbi/dsl/config.rbi
|
|
85
|
+
# typed: true
|
|
86
|
+
|
|
87
|
+
class Config
|
|
88
|
+
# Generated from sorbet SorbetTyped::Props
|
|
89
|
+
sig { params(timeout_seconds: ::Integer).void }
|
|
90
|
+
def initialize(timeout_seconds: T.unsafe(nil)); end
|
|
91
|
+
end
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Nilable prop without default — still required (statically)
|
|
95
|
+
|
|
96
|
+
Sorbet's native `T::Props` runtime behavior is that `T.nilable` **always implies `default: nil`**, with
|
|
97
|
+
[no way to opt out](https://sorbet.org/docs/tstruct#tnilable-without-implying-default-nil). At actual Ruby runtime, a
|
|
98
|
+
`T.nilable` prop without an explicit `default:`/`factory:` is optional — omitting it simply sets it to `nil`.
|
|
99
|
+
|
|
100
|
+
This gem's Tapioca compiler deliberately diverges from that for the **generated static signature**: it never treats a
|
|
101
|
+
nilable prop as optional unless `default:`/`factory:` is set explicitly. This means `srb tc` will flag a missing nilable
|
|
102
|
+
prop as a static type error, even though it would succeed at runtime. This is intentional — it surfaces "forgot to pass
|
|
103
|
+
this prop" mistakes at type-check time instead of letting them silently become `nil`.
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
class Notification
|
|
107
|
+
include SorbetTyped::Props
|
|
108
|
+
|
|
109
|
+
prop :message, T.nilable(String)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
Notification.new(message: nil) # ok
|
|
113
|
+
Notification.new # ok at runtime — message is nil
|
|
114
|
+
# But `srb tc` reports: Missing required keyword argument `message`
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Nilable prop with default nil — optional
|
|
118
|
+
|
|
119
|
+
Adding `default: nil` makes the prop optional. `nil` is both the type-safe value and the default.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
class Notification
|
|
123
|
+
include SorbetTyped::Props
|
|
124
|
+
|
|
125
|
+
prop :message, T.nilable(String), default: nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
Notification.new # ok — message is nil
|
|
129
|
+
Notification.new(message: 'hello') # ok
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Generated `initialize` signature:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# sorbet/rbi/dsl/notification.rbi
|
|
136
|
+
# typed: true
|
|
137
|
+
|
|
138
|
+
class Notification
|
|
139
|
+
# Generated from sorbet SorbetTyped::Props
|
|
140
|
+
sig { params(message: T.nilable(::String)).void }
|
|
141
|
+
def initialize(message: T.unsafe(nil)); end
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Const with factory — dependency injection
|
|
146
|
+
|
|
147
|
+
`factory:` provides a proc that constructs a fresh default value for each instance. Combined with `private`, this
|
|
148
|
+
creates a clean dependency injection point: the real implementation is used by default; tests can supply a test double
|
|
149
|
+
by passing it to the constructor.
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
class OrderRepository
|
|
153
|
+
include SorbetTyped::Props
|
|
154
|
+
|
|
155
|
+
const :database, Database, factory: -> { Database.connect }
|
|
156
|
+
const :cache, Cache, factory: -> { Cache.new }
|
|
157
|
+
private :database, :cache
|
|
158
|
+
|
|
159
|
+
def find(id)
|
|
160
|
+
cache.fetch(id) { database.query(id) }
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Production — real database and cache are created by the factory:
|
|
165
|
+
repo = OrderRepository.new
|
|
166
|
+
|
|
167
|
+
# Tests — inject test doubles:
|
|
168
|
+
repo = OrderRepository.new(database: test_db, cache: test_cache)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Generated `initialize` signature — both deps are optional (factory provides defaults):
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
# sorbet/rbi/dsl/order_repository.rbi
|
|
175
|
+
# typed: true
|
|
176
|
+
|
|
177
|
+
class OrderRepository
|
|
178
|
+
# Generated from sorbet SorbetTyped::Props
|
|
179
|
+
sig { params(database: Database, cache: Cache).void }
|
|
180
|
+
def initialize(database: T.unsafe(nil), cache: T.unsafe(nil)); end
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Behavior notes
|
|
185
|
+
|
|
186
|
+
- Props with `default:` or `factory:` become optional keyword arguments in the generated `initialize`. They appear after
|
|
187
|
+
all required props in the signature, regardless of declaration order.
|
|
188
|
+
- `factory:` is called once per object construction. It is safe for mutable objects (arrays, hashes, service instances).
|
|
189
|
+
- `default:` reuses the same object across all instances. Do **not** use `default: []` or `default: {}` — use
|
|
190
|
+
`factory: -> { [] }` instead.
|
|
191
|
+
- The Tapioca compiler treats `default:` and `factory:` identically — both produce optional keyword arguments in the
|
|
192
|
+
generated signature.
|
|
193
|
+
- For the difference between a `T.nilable` prop and an internal prop that is never in `initialize`, see
|
|
194
|
+
[Internal props](internal-props.md).
|
|
195
|
+
- Native sorbet `T::Props` runtime behavior: `T.nilable` always implies `default: nil`, with no opt-out
|
|
196
|
+
([sorbet docs](https://sorbet.org/docs/tstruct#tnilable-without-implying-default-nil)). This gem's static signature
|
|
197
|
+
generation intentionally does not mirror that — see
|
|
198
|
+
[Nilable prop without default — still required (statically)](#nilable-prop-without-default--still-required-statically).
|
data/docs/inheritance.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Inheritance
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Table of Contents](#table-of-contents)
|
|
6
|
+
- [What it does](#what-it-does)
|
|
7
|
+
- [When to use](#when-to-use)
|
|
8
|
+
- [Examples](#examples)
|
|
9
|
+
- [Subclass inherits parent props](#subclass-inherits-parent-props)
|
|
10
|
+
- [Child adds a required prop](#child-adds-a-required-prop)
|
|
11
|
+
- [Child adds a prop with a default](#child-adds-a-prop-with-a-default)
|
|
12
|
+
- [Behavior notes](#behavior-notes)
|
|
13
|
+
|
|
14
|
+
## What it does
|
|
15
|
+
|
|
16
|
+
Props declared in a parent class are inherited by subclasses. The Tapioca compiler generates a separate `initialize` RBI
|
|
17
|
+
for each concrete class that has props, including all inherited ones.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- You have a base class that defines common constructor parameters shared by all subclasses.
|
|
22
|
+
- You want subclasses to extend the constructor with additional props without repeating the parent's declarations.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Subclass inherits parent props
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
class Animal
|
|
30
|
+
include SorbetTyped::Props
|
|
31
|
+
|
|
32
|
+
prop :name, String
|
|
33
|
+
prop :weight, Float
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Dog < Animal
|
|
37
|
+
prop :breed, String
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Dog.new(name: 'Rex', weight: 30.5, breed: 'Labrador')
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Parent RBI — generated for `Animal`:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# sorbet/rbi/dsl/animal.rbi
|
|
47
|
+
# typed: true
|
|
48
|
+
|
|
49
|
+
class Animal
|
|
50
|
+
# Generated from sorbet SorbetTyped::Props
|
|
51
|
+
sig { params(name: ::String, weight: ::Float).void }
|
|
52
|
+
def initialize(name:, weight:); end
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Child RBI — includes both parent and child props:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
# sorbet/rbi/dsl/dog.rbi
|
|
60
|
+
# typed: true
|
|
61
|
+
|
|
62
|
+
class Dog < Animal
|
|
63
|
+
# Generated from sorbet SorbetTyped::Props
|
|
64
|
+
sig { params(name: ::String, weight: ::Float, breed: ::String).void }
|
|
65
|
+
def initialize(name:, weight:, breed:); end
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Child adds a required prop
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
class Vehicle
|
|
73
|
+
include SorbetTyped::Props
|
|
74
|
+
|
|
75
|
+
const :make, String
|
|
76
|
+
const :model, String
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class ElectricVehicle < Vehicle
|
|
80
|
+
const :range_km, Integer
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
ElectricVehicle.new(make: 'Tesla', model: 'Model 3', range_km: 450)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Generated child RBI:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
# sorbet/rbi/dsl/electric_vehicle.rbi
|
|
90
|
+
# typed: true
|
|
91
|
+
|
|
92
|
+
class ElectricVehicle < Vehicle
|
|
93
|
+
# Generated from sorbet SorbetTyped::Props
|
|
94
|
+
sig { params(make: ::String, model: ::String, range_km: ::Integer).void }
|
|
95
|
+
def initialize(make:, model:, range_km:); end
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Child adds a prop with a default
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
class Notification
|
|
103
|
+
include SorbetTyped::Props
|
|
104
|
+
|
|
105
|
+
const :recipient, String
|
|
106
|
+
const :subject, String
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class EmailNotification < Notification
|
|
110
|
+
const :priority, String, default: 'normal'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
EmailNotification.new(recipient: 'user@example.com', subject: 'Hello')
|
|
114
|
+
# priority defaults to 'normal'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Generated child RBI — `priority` is optional:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
# sorbet/rbi/dsl/email_notification.rbi
|
|
121
|
+
# typed: true
|
|
122
|
+
|
|
123
|
+
class EmailNotification < Notification
|
|
124
|
+
# Generated from sorbet SorbetTyped::Props
|
|
125
|
+
sig { params(recipient: ::String, subject: ::String, priority: ::String).void }
|
|
126
|
+
def initialize(recipient:, subject:, priority: T.unsafe(nil)); end
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Behavior notes
|
|
131
|
+
|
|
132
|
+
- Props are inherited in definition order: parent props come first in the generated `initialize` signature, followed by
|
|
133
|
+
child props.
|
|
134
|
+
- Required props appear before optional props (those with `default:` or `factory:`), regardless of declaration order
|
|
135
|
+
across parent and child.
|
|
136
|
+
- The Tapioca compiler generates a separate RBI for each concrete class. Each RBI includes all inherited props.
|
|
137
|
+
- Abstract classes (those with `abstract!`) are skipped by the Tapioca compiler — no `initialize` RBI is generated for
|
|
138
|
+
them.
|
|
139
|
+
- Props cannot be removed or re-typed in a subclass.
|