feat(type-info): add Partial, Required, and Readonly type synthesis#9875
feat(type-info): add Partial, Required, and Readonly type synthesis#9875ematipico merged 18 commits intobiomejs:mainfrom
Conversation
🦋 Changeset detectedLatest commit: a8d1800 The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis change extends the type inference system to properly resolve Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
crates/biome_js_type_info/src/local_inference.rs (2)
2353-2363:⚠️ Potential issue | 🟠 MajorUse
named_param.is_optionalinstead of hardcodingfalse.Constructor parameter properties lose their optionality here. The
NamedFunctionParametercarries the correct optional flag, but it's discarded when synthesising the class member. This breaks type inference for utilities likePartial<T>andRequired<T>.Fix
members.push(Self { kind: TypeMemberKind::Named(named_param.name.clone()), ty: param.parameter.ty().clone(), - is_optional: false, + is_optional: named_param.is_optional, is_readonly: false, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_js_type_info/src/local_inference.rs` around lines 2353 - 2363, The constructor parameter property synthesis currently hardcodes is_optional to false inside the loop over constructor.parameters (the branch matching FunctionParameter::Named(named_param)), which drops the optionality; change the created TypeMember (Self { ... }) to set is_optional using the NamedFunctionParameter flag (named_param.is_optional) instead of false so the synthesized class member preserves the parameter's optionality when constructing the member list in local_inference.rs.
1881-1913:⚠️ Potential issue | 🟠 MajorApply readonly modifier detection to
JsPropertyClassMemberto matchTsPropertySignatureClassMember.At line 1913,
JsPropertyClassMemberpassesfalsefor the readonly parameter, missingclass C { readonly x: number }syntax. The same detection pattern already works at line 1974–1977 forTsPropertySignatureClassMember; reuse it here.Suggested fix
let is_static = member .modifiers() .into_iter() .any(|modifier| modifier.as_js_static_modifier().is_some()); + let is_readonly = member + .modifiers() + .into_iter() + .any(|modifier| modifier.as_ts_readonly_modifier().is_some()); let is_optional = member .property_annotation() .as_ref() .and_then(|annotation| annotation.as_ts_optional_property_annotation()) .is_some(); Self::from_class_member_info( resolver, scope_id, name, ty, is_static, is_optional, - false, + is_readonly, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_js_type_info/src/local_inference.rs` around lines 1881 - 1913, The JsPropertyClassMember branch currently passes a hardcoded false for the readonly flag to Self::from_class_member_info; detect readonly the same way as the TsPropertySignatureClassMember branch by examining member.modifiers() for a TS readonly modifier (e.g. let is_readonly = member.modifiers().into_iter().any(|m| m.as_ts_readonly_modifier().is_some());) and pass is_readonly instead of false to from_class_member_info for AnyJsClassMember::JsPropertyClassMember.
🧹 Nitpick comments (1)
crates/biome_js_type_info/src/globals.rs (1)
40-41: Optional tidy-up: centralise defaultTypeMemberflags.All these updates are correct; consider a tiny helper/builder for “plain” members so future field additions don’t require a hunt across many call sites.
Also applies to: 123-124, 130-131, 208-209, 258-259, 459-460, 489-490
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_js_type_info/src/globals.rs` around lines 40 - 41, Multiple places set identical default flags for TypeMember (is_optional: false, is_readonly: false); introduce a small helper factory to centralize those defaults and update all call sites. Add a constructor or associated function like TypeMember::plain(name, ty) or a helper plain_member(...) that sets is_optional and is_readonly to false and fills the other common fields, then replace the repeated struct literals in globals.rs (the places constructing TypeMember instances) to call that helper (e.g., use TypeMember::plain or plain_member for the members currently using is_optional/is_readonly flags).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/biome_js_type_info/src/resolver.rs`:
- Around line 819-835: The code creates a new TypeMember but only normalises
member.ty with resolved.apply_module_id_to_reference(...), leaving member.kind
(especially TypeMemberKind::IndexSignature(...)) containing raw references that
point to the wrong module; instead clone and normalise the entire member so keys
get the correct ResolverId. Update the construction of TypeMember to start from
a fully normalised member (e.g. clone a member that has had
apply_module_id_to_reference applied to all internal references) rather than
only normalising ty, and ensure whenever you call ResolvedTypeData.as_raw_data()
you pass or apply the correct ResolverId so subsequent resolution calls don’t
panic.
- Around line 958-976: In strip_undefined_from_member, resolve each union arm
via resolver.resolve_and_get before testing for undefined instead of comparing
raw TypeReference values to GLOBAL_UNDEFINED_ID; iterate union.0, call
resolver.resolve_and_get on each variant and drop arms whose resolved raw data
is TypeData::Primitive/Undefined (or resolves to GLOBAL_UNDEFINED_ID), then if
one arm remains set member.ty to that arm's TypeReference, else if some arms
were removed register a new Union via resolver.register_and_resolve and set
member.ty to TypeReference::from(new_id) (do not rebuild from raw union.0
directly); when calling resolved.as_raw_data() ensure you use the
ResolvedTypeData tied to the correct ResolverId (use the resolved value returned
from resolver.resolve_and_get) to preserve resolver context and avoid panics,
and handle the all-undefined case by registering an appropriate empty/undefined
union result rather than constructing an empty Union raw vector.
---
Outside diff comments:
In `@crates/biome_js_type_info/src/local_inference.rs`:
- Around line 2353-2363: The constructor parameter property synthesis currently
hardcodes is_optional to false inside the loop over constructor.parameters (the
branch matching FunctionParameter::Named(named_param)), which drops the
optionality; change the created TypeMember (Self { ... }) to set is_optional
using the NamedFunctionParameter flag (named_param.is_optional) instead of false
so the synthesized class member preserves the parameter's optionality when
constructing the member list in local_inference.rs.
- Around line 1881-1913: The JsPropertyClassMember branch currently passes a
hardcoded false for the readonly flag to Self::from_class_member_info; detect
readonly the same way as the TsPropertySignatureClassMember branch by examining
member.modifiers() for a TS readonly modifier (e.g. let is_readonly =
member.modifiers().into_iter().any(|m| m.as_ts_readonly_modifier().is_some());)
and pass is_readonly instead of false to from_class_member_info for
AnyJsClassMember::JsPropertyClassMember.
---
Nitpick comments:
In `@crates/biome_js_type_info/src/globals.rs`:
- Around line 40-41: Multiple places set identical default flags for TypeMember
(is_optional: false, is_readonly: false); introduce a small helper factory to
centralize those defaults and update all call sites. Add a constructor or
associated function like TypeMember::plain(name, ty) or a helper
plain_member(...) that sets is_optional and is_readonly to false and fills the
other common fields, then replace the repeated struct literals in globals.rs
(the places constructing TypeMember instances) to call that helper (e.g., use
TypeMember::plain or plain_member for the members currently using
is_optional/is_readonly flags).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 45cc2c21-7663-4427-9798-e5cae9829109
⛔ Files ignored due to path filters (6)
crates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/valid.ts.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_export_const_type_declaration_with_namespace.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_resolve_merged_namespace_with_type.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_resolve_recursive_looking_country_info.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_resolve_recursive_looking_vfile.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (11)
crates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/invalid.tscrates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/valid.tscrates/biome_js_type_info/src/flattening/expressions.rscrates/biome_js_type_info/src/flattening/intersections.rscrates/biome_js_type_info/src/globals.rscrates/biome_js_type_info/src/local_inference.rscrates/biome_js_type_info/src/resolver.rscrates/biome_js_type_info/src/type_data.rscrates/biome_js_type_info/tests/type_data.rscrates/biome_js_type_info_macros/src/resolvable_derive.rscrates/biome_module_graph/src/js_module_info/collector.rs
7292a66 to
45d7874
Compare
ematipico
left a comment
There was a problem hiding this comment.
Same thing as the other PR:
- tests
- changeset
I'll check the code once it's ready
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/invalid.ts (1)
85-87: Optional follow-up: add one composed utility-type fixture.
A case likeReadonly<Required<Partial<{ a: string | null }>>>with||would lock in composition behaviour and catch mapper-chaining regressions early.As per coding guidelines, "All code changes MUST include appropriate tests: ... bug fixes require tests that reproduce and validate the fix."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/invalid.ts` around lines 85 - 87, Add a composed utility-type fixture that uses chained mapped/utility types and the || operator to catch mapper-chaining regressions: create a test similar to the existing `roOpt` case but declaring a variable like `const composed: Readonly<Required<Partial<{ a: string | null }>>>` (or a similarly named symbol) and then use `composed.a || "default"` (store result in a variable such as `vComposed`) so the analyzer exercises composition of Readonly/Required/Partial with a nullable property; add this to the same specs directory as the other fixtures so the test suite validates the composed-type behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/invalid.ts`:
- Around line 85-87: Add a composed utility-type fixture that uses chained
mapped/utility types and the || operator to catch mapper-chaining regressions:
create a test similar to the existing `roOpt` case but declaring a variable like
`const composed: Readonly<Required<Partial<{ a: string | null }>>>` (or a
similarly named symbol) and then use `composed.a || "default"` (store result in
a variable such as `vComposed`) so the analyzer exercises composition of
Readonly/Required/Partial with a nullable property; add this to the same specs
directory as the other fixtures so the test suite validates the composed-type
behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b67ac57d-0333-4f63-9317-3996adaa91a2
⛔ Files ignored due to path filters (8)
crates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noMisusedPromises/checksConditionalsInvalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUnnecessaryConditions/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUnsafePlusOperands/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUselessTypeConversion/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/useArraySortCompare/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/useExhaustiveSwitchCases/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/invalid.ts.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (9)
.changeset/young-tips-swim.mdcrates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/invalid.tscrates/biome_js_analyze/tests/specs/nursery/noMisusedPromises/checksConditionalsInvalid.tscrates/biome_js_analyze/tests/specs/nursery/noUnnecessaryConditions/invalid.tscrates/biome_js_analyze/tests/specs/nursery/noUnsafePlusOperands/invalid.tscrates/biome_js_analyze/tests/specs/nursery/noUselessTypeConversion/invalid.tscrates/biome_js_analyze/tests/specs/nursery/useArraySortCompare/invalid.tscrates/biome_js_analyze/tests/specs/nursery/useExhaustiveSwitchCases/invalid.tscrates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/invalid.ts
✅ Files skipped from review due to trivial changes (1)
- .changeset/young-tips-swim.md
…ol in derive macro
ff9e04e to
6e0bf5d
Compare
@ematipico Same structure as #9874 - tests for the 8 type-aware rules in ac046f0, changeset in 6e0bf5d. |
ematipico
left a comment
There was a problem hiding this comment.
I think the entity of the change isn't there yet. We increased the memory usage. We should explore different solutions
| is_optional: false, | ||
| is_readonly: false, |
There was a problem hiding this comment.
It might be nice to add new code so that we can see if the new fields are correctly populated
| @@ -59,7 +59,7 @@ fn verify_type_sizes() { | |||
|
|
|||
| assert_eq!( | |||
| std::mem::size_of::<TypeMember>(), | |||
| 40, | |||
| 48, | |||
There was a problem hiding this comment.
This test is here for a reason; we can't increase this type lightly. Have you considered storing the flags somewhere else?
There was a problem hiding this comment.
Moved to TypeMemberKind::NamedOptional, 40 bytes restored. 09e12c8
| @@ -880,3 +964,24 @@ macro_rules! derive_primitive_resolved { | |||
| } | |||
|
|
|||
| derive_primitive_resolved!(bool, f64, u32, u64, usize); | |||
|
|
|||
| fn strip_undefined_from_member(resolver: &mut dyn TypeResolver, member: &mut TypeMember) { | |||
| } else if (qualifier.is_partial() | ||
| || qualifier.is_required() | ||
| || qualifier.is_readonly_type()) | ||
| && qualifier.type_parameters.len() == 1 |
There was a problem hiding this comment.
Do they need to be handled all in the same branch? For example is_readonly isn't checked against the other two flags.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
crates/biome_js_type_info/src/resolver.rs (1)
1020-1025:⚠️ Potential issue | 🟠 MajorUnion arms still compared as raw references without resolving.
The filtering logic compares
**v != TypeReference::from(GLOBAL_UNDEFINED_ID)against raw references fromunion.0. Ifundefinedarrives through an aliased reference path, it won't be filtered out. Additionally, the cloned references lack module ID normalisation.Consider resolving each variant before testing for undefined, and applying module IDs when rebuilding the filtered union. Based on learnings: when iterating over union variant
TypeReferences, always resolve the variant viaresolver.resolve_and_get()first rather than comparing rawTypeReferences against global IDs.Sketch of approach
let resolver_id = resolved.resolver_id(); let filtered: Vec<_> = union .0 .iter() .filter(|v| { let normalised = resolver_id.apply_module_id_to_reference(v); resolver .resolve_and_get(&normalised) .map_or(true, |r| !matches!(r.as_raw_data(), TypeData::Undefined)) }) .map(|v| resolver_id.apply_module_id_to_reference(v).into_owned()) .collect();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_js_type_info/src/resolver.rs` around lines 1020 - 1025, The current union filtering compares raw TypeReference values and clones them directly, which misses aliased/normalized undefineds and skips module ID normalization; change the loop over union.0 to first obtain resolver_id via resolved.resolver_id(), apply module ID normalization to each variant using resolver_id.apply_module_id_to_reference(), then call resolver.resolve_and_get(&normalised) and filter out any resolved variant whose as_raw_data() is TypeData::Undefined; finally, rebuild the filtered Vec by mapping each original variant to the normalized (owned) TypeReference so the reconstructed union uses normalized module IDs (use resolver.resolve_and_get(), resolver_id.apply_module_id_to_reference(), TypeData::Undefined checks, and avoid direct comparison to GLOBAL_UNDEFINED_ID).
🧹 Nitpick comments (1)
crates/biome_module_graph/tests/spec_tests.rs (1)
2623-2646: Please also hit utility-type synthesis in this fixture.Good baseline test, but it currently validates member parsing only. Add
Partial<Config>,Required<Config>, andReadonly<Config>usages so this snapshot protects the exact synthesis path introduced by this PR.Suggested fixture tweak
fn test_optional_and_readonly_members() { let fs = MemoryFileSystem::default(); fs.insert( "/src/index.ts".into(), r#"export interface Config { name: string; age?: number; readonly id: string; readonly label?: string; } + +export type ConfigPartial = Partial<Config>; +export type ConfigRequired = Required<Config>; +export type ConfigReadonly = Readonly<Config>; "#, );As per coding guidelines, “All code changes MUST include appropriate tests … bug fixes require tests that reproduce and validate the fix.”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/tests/spec_tests.rs` around lines 2623 - 2646, The test currently only defines interface Config but doesn't exercise utility-type synthesis; update the fixture inserted into MemoryFileSystem in the test_optional_and_readonly_members test so the /src/index.ts content also references Partial<Config>, Required<Config>, and Readonly<Config> (for example create exported type aliases like ExportedPartial = Partial<Config>, ExportedRequired = Required<Config>, ExportedReadonly = Readonly<Config>) so ModuleGraph::update_graph_for_js_paths and ModuleGraphSnapshot::new will synthesize those utility types and the snapshot ("test_optional_and_readonly_members") will capture the new synthesis path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/biome_js_type_info/src/local_inference.rs`:
- Around line 2265-2267: The current member-kind encoding prioritizes is_static
over is_optional causing `static foo?: T` to be encoded as NamedStatic and lose
optional semantics; update the enum TypeMemberKind to include a
NamedStaticOptional variant (or otherwise ensure both flags are represented),
change from_class_member_info to emit NamedStaticOptional when both is_static
and is_optional are true, and then update TypeMember::is_optional(),
TypeMemberKind::is_static(), the formatter output for the new variant, and the
resolver transforms that handle Partial<T>/Required<T> so they respect the new
combined variant.
---
Duplicate comments:
In `@crates/biome_js_type_info/src/resolver.rs`:
- Around line 1020-1025: The current union filtering compares raw TypeReference
values and clones them directly, which misses aliased/normalized undefineds and
skips module ID normalization; change the loop over union.0 to first obtain
resolver_id via resolved.resolver_id(), apply module ID normalization to each
variant using resolver_id.apply_module_id_to_reference(), then call
resolver.resolve_and_get(&normalised) and filter out any resolved variant whose
as_raw_data() is TypeData::Undefined; finally, rebuild the filtered Vec by
mapping each original variant to the normalized (owned) TypeReference so the
reconstructed union uses normalized module IDs (use resolver.resolve_and_get(),
resolver_id.apply_module_id_to_reference(), TypeData::Undefined checks, and
avoid direct comparison to GLOBAL_UNDEFINED_ID).
---
Nitpick comments:
In `@crates/biome_module_graph/tests/spec_tests.rs`:
- Around line 2623-2646: The test currently only defines interface Config but
doesn't exercise utility-type synthesis; update the fixture inserted into
MemoryFileSystem in the test_optional_and_readonly_members test so the
/src/index.ts content also references Partial<Config>, Required<Config>, and
Readonly<Config> (for example create exported type aliases like ExportedPartial
= Partial<Config>, ExportedRequired = Required<Config>, ExportedReadonly =
Readonly<Config>) so ModuleGraph::update_graph_for_js_paths and
ModuleGraphSnapshot::new will synthesize those utility types and the snapshot
("test_optional_and_readonly_members") will capture the new synthesis path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b08943b4-1e53-4161-bb70-e8b123bd9ecf
⛔ Files ignored due to path filters (2)
crates/biome_module_graph/tests/snapshots/test_optional_and_readonly_members.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_resolve_recursive_looking_vfile.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (5)
crates/biome_js_type_info/src/format_type_info.rscrates/biome_js_type_info/src/local_inference.rscrates/biome_js_type_info/src/resolver.rscrates/biome_js_type_info/src/type_data.rscrates/biome_module_graph/tests/spec_tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/biome_js_type_info/src/type_data.rs
| } else if is_optional { | ||
| TypeMemberKind::NamedOptional(name) | ||
| } else { |
There was a problem hiding this comment.
static + optional is still lossy in member-kind encoding.
On Line 2265, is_static wins before is_optional, so static foo?: T is encoded as NamedStatic, not optional. That drops optional-kind semantics used elsewhere (e.g. is_optional() checks).
Direction for a robust fix
- pub enum TypeMemberKind {
+ pub enum TypeMemberKind {
CallSignature,
Constructor,
Getter(Text),
IndexSignature(TypeReference),
Named(Text),
NamedOptional(Text),
NamedStatic(Text),
+ NamedStaticOptional(Text),
}Then update:
TypeMember::is_optional()andTypeMemberKind::is_static()- formatter output for the new variant
- resolver transforms for
Partial<T>/Required<T> from_class_member_infoto emitNamedStaticOptionalwhen both flags are true.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/biome_js_type_info/src/local_inference.rs` around lines 2265 - 2267,
The current member-kind encoding prioritizes is_static over is_optional causing
`static foo?: T` to be encoded as NamedStatic and lose optional semantics;
update the enum TypeMemberKind to include a NamedStaticOptional variant (or
otherwise ensure both flags are represented), change from_class_member_info to
emit NamedStaticOptional when both is_static and is_optional are true, and then
update TypeMember::is_optional(), TypeMemberKind::is_static(), the formatter
output for the new variant, and the resolver transforms that handle
Partial<T>/Required<T> so they respect the new combined variant.
6d0ba70 to
be54031
Compare
be54031 to
f9170e2
Compare
…tional-readonly # Conflicts: # crates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/invalid.ts # crates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/invalid.ts.snap # crates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/valid.ts # crates/biome_js_analyze/tests/specs/nursery/noMisleadingReturnType/valid.ts.snap
@ematipico Dropped both bool fields, optionality is now a |
I used Claude Code to assist with this implementation.
Summary
Addresses items from #9810: adds
is_optionalandis_readonlytoTypeMember, synthesizesPartial<T>,Required<T>, andReadonly<T>.Parses
readonlymodifiers from source. Stripsundefinedfrom member types forRequired<T>.Test Plan
Snapshot tests.