sorbet_typed-props 1.5.5 → 1.5.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7206c19cf06589c26a6d00a673273645270a37d4957304902ee0a3eb5e5cfe9
4
- data.tar.gz: 44091faa0e3dbf390cb383dbed54044230cb1416eaee7a4501003d1209622183
3
+ metadata.gz: 0ea0b943a9c67c764b3a231c7dee5a7067f217c6b0affbe307d41276085e243d
4
+ data.tar.gz: b2e761a7cc064ca3784e7f2315c916ef52e7dd92350219e206c23ba1df173c69
5
5
  SHA512:
6
- metadata.gz: d196157b6961f084f2ef8b60e77348d17b2a7d72f6a75a990bdaff20747ff80807a3ca5d091dab0a1f7e072dc36a9f44fdd510e9e88205d968cee1d9359907b6
7
- data.tar.gz: ce46d5dc413eb1da669a9c98a24051c56c2cbf741952a4445e55ff237a70900ea0372f7456e59caab3f4c10746babdf57e1c0b93ff5c504ffa10b76c3fa10192
6
+ metadata.gz: becef28ed4be57479118206763a662d5bb2c98791000bcd7d8cdd7d1e987f31fe90cce6311f81f06a7a782c336cfea2ab58cb2798736accb1ba2258bbc5fd149
7
+ data.tar.gz: 7b1f00d89e420e169273c7686ee8fad7acbb6234233869fa7cb9b6c35ce05451041aa69ffb109c8e9ab0a5eb5541b2650f353688d54178064a5ce4191f7f243d
data/.cz.yaml CHANGED
@@ -15,7 +15,7 @@ commitizen:
15
15
  - mise run format
16
16
  tag_format: v$version
17
17
  update_changelog_on_bump: true
18
- version: 1.5.5
18
+ version: 1.5.6
19
19
  version_files:
20
20
  - lib/sorbet_typed/props/version.rb
21
21
  version_scheme: semver2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## v1.5.6 (2026-07-29)
2
+
3
+ ### Fix
4
+
5
+ - **tapioca**: stop generating invalid self.new RBI for generic classes
6
+
1
7
  ## v1.5.5 (2026-07-17)
2
8
 
3
9
  ### Fix
data/docs/phlex.md CHANGED
@@ -9,6 +9,7 @@
9
9
  - [Phlex component with props](#phlex-component-with-props)
10
10
  - [Behavior notes](#behavior-notes)
11
11
  - [Automatic `::new` typing](#automatic-new-typing)
12
+ - [When `::new` cannot be typed](#when-new-cannot-be-typed)
12
13
  - [Phlex version compatibility](#phlex-version-compatibility)
13
14
  - [Element-name collisions](#element-name-collisions)
14
15
 
@@ -78,6 +79,33 @@ calls exactly like it validates `#initialize` calls on non-Phlex classes — no
78
79
  Components::UserCard.new # error: Missing required keyword argument `name`
79
80
  ```
80
81
 
82
+ Note that this `self.new` generation only kicks in for classes whose ancestor genuinely overrides `::new` with a real
83
+ method (like `Phlex::SGML`) - a class descending from an _abstract_ ancestor (`abstract!`/`interface!`) does not need
84
+ one: Sorbet already infers `.new` correctly from `#initialize` there, even though the class also happens to inherit a
85
+ runtime-only `self.new` guard from `abstract!`.
86
+
87
+ ## When `::new` cannot be typed
88
+
89
+ If a prop's type is one of the class's own `type_member`s/`type_template`s _and_ an ancestor overrides `::new`, the gem
90
+ cannot generate a valid `self.new` signature: Sorbet forbids referencing a `type_member` inside a singleton method
91
+ signature (only `type_template` is allowed there, see [srb.help/5072](https://srb.help/5072)). This combination raises a
92
+ `RuntimeError` while generating RBI files, rather than silently emitting invalid RBI or silently omitting `self.new`
93
+ (which would leave `.new` resolving against the wrong, Phlex-overridden signature).
94
+
95
+ ```ruby
96
+ class Components::GenericCard < Phlex::HTML # Phlex::SGML overrides `::new`
97
+ extend T::Generic
98
+ include SorbetTyped::Props
99
+
100
+ DataType = type_member { { upper: Object } }
101
+
102
+ prop :data, DataType # raises when generating RBI files - `DataType` can't appear in a `self.new` signature
103
+ end
104
+ ```
105
+
106
+ To fix this, either give the prop a concrete (non-generic) type, or avoid mixing `type_member`-typed props into a class
107
+ whose ancestor overrides `::new`.
108
+
81
109
  ## Phlex version compatibility
82
110
 
83
111
  `SorbetTyped::Props` requires Phlex `>= 1.0.0` when included in a Phlex component. Versions before 1.0 predate the
@@ -5,7 +5,7 @@
5
5
 
6
6
  module SorbetTyped
7
7
  module Props
8
- VERSION = '1.5.5'
8
+ VERSION = '1.5.6'
9
9
  end
10
10
  end
11
11
 
@@ -67,16 +67,9 @@ module Tapioca
67
67
 
68
68
  sig { params(name: Symbol, details: T.untyped).returns(RBI::TypedParam) }
69
69
  def create_param_from_prop(name:, details:)
70
- details_struct =
71
- Tapioca::Dsl::Compilers::SorbetTypedPropsConstructor::PropToParamConverter::Details.
72
- from_hash(details.transform_keys(&:to_s))
73
-
74
- Tapioca::Dsl::Compilers::SorbetTypedPropsConstructor::PropToParamConverter.
75
- new(
76
- name:,
77
- details: details_struct
78
- ).
79
- create_rbi_parameter
70
+ details_struct = PropToParamConverter::Details.from_hash(details.transform_keys(&:to_s))
71
+
72
+ PropToParamConverter.new(name:, details: details_struct).create_rbi_parameter
80
73
  end
81
74
 
82
75
  sig { void }
@@ -94,6 +87,8 @@ module Tapioca
94
87
 
95
88
  next unless self_new_overridden?
96
89
 
90
+ raise_unsupported_type_variable_new if references_type_variable?
91
+
97
92
  klass.create_method(
98
93
  'new',
99
94
  parameters: params_with_optional_last,
@@ -104,14 +99,42 @@ module Tapioca
104
99
  end
105
100
  end
106
101
 
102
+ sig { returns(T.noreturn) }
103
+ # An ancestor overrides `self.new`, so we'd normally define our own to
104
+ # win ancestor-chain resolution - but a param is typed with one of
105
+ # this class's own type variables, illegal in a singleton signature
106
+ # (https://srb.help/5072). Raise rather than emit invalid RBI.
107
+ def raise_unsupported_type_variable_new
108
+ raise "#{constant}: cannot generate a `self.new` signature - an ancestor overrides `self.new`, " \
109
+ "but the initializer has a parameter typed with one of this class's own " \
110
+ '`type_member`s/`type_template`s (https://srb.help/5072). Give the prop a concrete ' \
111
+ '(non-generic) type, or drop the `self.new`-overriding ancestor.'
112
+ end
113
+
107
114
  sig { returns(T::Boolean) }
108
- # NOTE: Phlex::SGML overrides `self.new` (untyped), so Sorbet resolves
109
- # `.new` calls against that instead of synthesizing a signature from
110
- # `#initialize`. Defining our own `self.new` directly on the leaf class
111
- # wins ancestor-chain resolution over any such override. Gated so plain
112
- # classes (the common case) get no extra RBI output.
115
+ # Phlex::SGML overrides `self.new` (untyped); defining our own wins
116
+ # ancestor-chain resolution. Excludes `abstract!`'s own `self.new`
117
+ # guard (Sorbet already infers `.new` through it) - otherwise every
118
+ # concrete descendant of an abstract class would false-positive here.
113
119
  def self_new_overridden?
114
- constant.method(:new).owner != Class
120
+ new_method_owner = T.unsafe(constant).method(:new).owner
121
+ return false if new_method_owner == Class
122
+ return false if abstract_ancestor?(new_method_owner)
123
+
124
+ true
125
+ end
126
+
127
+ sig { params(singleton_class: T.untyped).returns(T::Boolean) }
128
+ def abstract_ancestor?(singleton_class)
129
+ T::AbstractUtils.abstract_module?(singleton_class.attached_object)
130
+ end
131
+
132
+ sig { returns(T::Boolean) }
133
+ # A `type_member`/`type_template` cannot appear in a singleton method
134
+ # signature (https://srb.help/5072) - detects whether any param
135
+ # references one of the class's own type variables.
136
+ def references_type_variable?
137
+ props_in_initializer.each_value.any? { |rules| rules[:type_object].is_a?(T::Types::TypeVariable) }
115
138
  end
116
139
 
117
140
  sig { returns(T::Array[RBI::TypedParam]) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet_typed-props
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.5
4
+ version: 1.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Kramer