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 +4 -4
- data/.cz.yaml +1 -1
- data/CHANGELOG.md +6 -0
- data/docs/phlex.md +28 -0
- data/lib/sorbet_typed/props/version.rb +1 -1
- data/lib/tapioca/dsl/compilers/sorbet_typed_props_constructor.rb +39 -16
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0ea0b943a9c67c764b3a231c7dee5a7067f217c6b0affbe307d41276085e243d
|
|
4
|
+
data.tar.gz: b2e761a7cc064ca3784e7f2315c916ef52e7dd92350219e206c23ba1df173c69
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: becef28ed4be57479118206763a662d5bb2c98791000bcd7d8cdd7d1e987f31fe90cce6311f81f06a7a782c336cfea2ab58cb2798736accb1ba2258bbc5fd149
|
|
7
|
+
data.tar.gz: 7b1f00d89e420e169273c7686ee8fad7acbb6234233869fa7cb9b6c35ce05451041aa69ffb109c8e9ab0a5eb5541b2650f353688d54178064a5ce4191f7f243d
|
data/.cz.yaml
CHANGED
data/CHANGELOG.md
CHANGED
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
|
|
@@ -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
|
-
|
|
72
|
-
|
|
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
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
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
|
|
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]) }
|