Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
feat: Support extends constraints on infer type (#4018)
Browse files Browse the repository at this point in the history
Co-authored-by: Micha Reiser <micha@rome.tools>
  • Loading branch information
nissy-dev and MichaReiser authored Dec 30, 2022
1 parent 0a28b1b commit c7e0fa6
Show file tree
Hide file tree
Showing 15 changed files with 1,053 additions and 313 deletions.
36 changes: 28 additions & 8 deletions crates/rome_js_factory/src/generated/node_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion crates/rome_js_factory/src/generated/syntax_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 38 additions & 38 deletions crates/rome_js_formatter/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8103,6 +8103,44 @@ impl IntoFormat<JsFormatContext> for rome_js_syntax::TsTypeParameterName {
)
}
}
impl FormatRule<rome_js_syntax::TsTypeConstraintClause>
for crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause
{
type Context = JsFormatContext;
#[inline(always)]
fn fmt(
&self,
node: &rome_js_syntax::TsTypeConstraintClause,
f: &mut JsFormatter,
) -> FormatResult<()> {
FormatNodeRule::<rome_js_syntax::TsTypeConstraintClause>::fmt(self, node, f)
}
}
impl AsFormat<JsFormatContext> for rome_js_syntax::TsTypeConstraintClause {
type Format<'a> = FormatRefWithRule<
'a,
rome_js_syntax::TsTypeConstraintClause,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause,
>;
fn format(&self) -> Self::Format<'_> {
FormatRefWithRule::new(
self,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause::default(),
)
}
}
impl IntoFormat<JsFormatContext> for rome_js_syntax::TsTypeConstraintClause {
type Format = FormatOwnedWithRule<
rome_js_syntax::TsTypeConstraintClause,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause,
>;
fn into_format(self) -> Self::Format {
FormatOwnedWithRule::new(
self,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause::default(),
)
}
}
impl FormatRule<rome_js_syntax::TsPredicateReturnType>
for crate::ts::types::predicate_return_type::FormatTsPredicateReturnType
{
Expand Down Expand Up @@ -8251,44 +8289,6 @@ impl IntoFormat<JsFormatContext> for rome_js_syntax::TsTypeParameter {
)
}
}
impl FormatRule<rome_js_syntax::TsTypeConstraintClause>
for crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause
{
type Context = JsFormatContext;
#[inline(always)]
fn fmt(
&self,
node: &rome_js_syntax::TsTypeConstraintClause,
f: &mut JsFormatter,
) -> FormatResult<()> {
FormatNodeRule::<rome_js_syntax::TsTypeConstraintClause>::fmt(self, node, f)
}
}
impl AsFormat<JsFormatContext> for rome_js_syntax::TsTypeConstraintClause {
type Format<'a> = FormatRefWithRule<
'a,
rome_js_syntax::TsTypeConstraintClause,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause,
>;
fn format(&self) -> Self::Format<'_> {
FormatRefWithRule::new(
self,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause::default(),
)
}
}
impl IntoFormat<JsFormatContext> for rome_js_syntax::TsTypeConstraintClause {
type Format = FormatOwnedWithRule<
rome_js_syntax::TsTypeConstraintClause,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause,
>;
fn into_format(self) -> Self::Format {
FormatOwnedWithRule::new(
self,
crate::ts::auxiliary::type_constraint_clause::FormatTsTypeConstraintClause::default(),
)
}
}
impl FormatRule<rome_js_syntax::TsDefaultTypeClause>
for crate::ts::auxiliary::default_type_clause::FormatTsDefaultTypeClause
{
Expand Down
72 changes: 66 additions & 6 deletions crates/rome_js_formatter/src/parentheses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ use crate::utils::{AnyJsBinaryLikeExpression, AnyJsBinaryLikeLeftExpression};

use rome_js_syntax::{
AnyJsAssignment, AnyJsAssignmentPattern, AnyJsExpression, AnyJsFunctionBody,
AnyJsLiteralExpression, AnyTsType, JsArrowFunctionExpression, JsAssignmentExpression,
JsBinaryExpression, JsBinaryOperator, JsComputedMemberAssignment, JsComputedMemberExpression,
JsConditionalExpression, JsLanguage, JsParenthesizedAssignment, JsParenthesizedExpression,
JsPrivateName, JsSequenceExpression, JsStaticMemberAssignment, JsStaticMemberExpression,
JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, TsConditionalType, TsIndexedAccessType,
TsIntersectionTypeElementList, TsParenthesizedType, TsUnionTypeVariantList,
AnyJsLiteralExpression, AnyTsReturnType, AnyTsType, JsArrowFunctionExpression,
JsAssignmentExpression, JsBinaryExpression, JsBinaryOperator, JsComputedMemberAssignment,
JsComputedMemberExpression, JsConditionalExpression, JsLanguage, JsParenthesizedAssignment,
JsParenthesizedExpression, JsPrivateName, JsSequenceExpression, JsStaticMemberAssignment,
JsStaticMemberExpression, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, TsConditionalType,
TsConstructorType, TsFunctionType, TsIndexedAccessType, TsIntersectionTypeElementList,
TsParenthesizedType, TsUnionTypeVariantList,
};
use rome_rowan::{declare_node_union, match_ast, AstNode, AstSeparatedList, SyntaxResult};

Expand Down Expand Up @@ -725,6 +726,65 @@ pub(crate) fn is_check_type(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool
}
}

/// Tests if `node` is the extends type of a [TsConditionalType]
///
/// ```javascript
/// type s = A extends string ? boolean : number // true for `string`, false for `A`, `boolean` and `number`
/// ```
fn is_extends_type(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool {
debug_assert_is_parent(node, parent);

match parent.kind() {
JsSyntaxKind::TS_CONDITIONAL_TYPE => {
let conditional = TsConditionalType::unwrap_cast(parent.clone());

conditional
.extends_type()
.map(AstNode::into_syntax)
.as_ref()
== Ok(node)
}
_ => false,
}
}

/// Tests if `node` includes inferred return types with extends constraints
///
/// ```javascript
/// type Type<A> = A extends ((a: string) => infer B extends string) ? B : never; // true
/// ```
pub(crate) fn is_includes_inferred_return_types_with_extends_constraints(
node: &JsSyntaxNode,
parent: &JsSyntaxNode,
) -> bool {
if is_extends_type(node, parent) {
let return_type = match node.kind() {
JsSyntaxKind::TS_FUNCTION_TYPE => {
match TsFunctionType::unwrap_cast(node.clone()).return_type() {
Ok(AnyTsReturnType::AnyTsType(any)) => Ok(any),
_ => {
return false;
}
}
}
JsSyntaxKind::TS_CONSTRUCTOR_TYPE => {
TsConstructorType::unwrap_cast(node.clone()).return_type()
}

_ => {
return false;
}
};

match return_type {
Ok(AnyTsType::TsInferType(infer_type)) => infer_type.constraint().is_some(),
_ => false,
}
} else {
false
}
}

/// Returns `true` if node is in a union or intersection type with more than one variant
///
/// ```javascript
Expand Down
2 changes: 2 additions & 0 deletions crates/rome_js_formatter/src/ts/types/function_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::prelude::*;
use crate::js::declarations::function_declaration::should_group_function_parameters;
use crate::parentheses::{
is_check_type, is_in_many_type_union_or_intersection_list,
is_includes_inferred_return_types_with_extends_constraints,
operator_type_or_higher_needs_parens, NeedsParentheses,
};
use rome_formatter::write;
Expand Down Expand Up @@ -78,6 +79,7 @@ pub(super) fn function_like_type_needs_parentheses(
}
_ => {
is_check_type(node, parent)
|| is_includes_inferred_return_types_with_extends_constraints(node, parent)
|| operator_type_or_higher_needs_parens(node, parent)
|| is_in_many_type_union_or_intersection_list(node, parent)
}
Expand Down
12 changes: 10 additions & 2 deletions crates/rome_js_formatter/src/ts/types/infer_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@ impl FormatNodeRule<TsInferType> for FormatTsInferType {
fn fmt_fields(&self, node: &TsInferType, f: &mut JsFormatter) -> FormatResult<()> {
let TsInferTypeFields {
infer_token,
type_parameter,
name,
constraint,
} = node.as_fields();
write![f, [infer_token.format(), space(), type_parameter.format()]]

write!(f, [infer_token.format(), space(), name.format()])?;

if let Some(constraint) = constraint {
write!(f, [space(), constraint.format()])?;
}

Ok(())
}

fn needs_parentheses(&self, item: &TsInferType) -> bool {
Expand Down
Loading

0 comments on commit c7e0fa6

Please sign in to comment.