Skip to content

Commit

Permalink
Type-safe updaters for TypeScript (#4370)
Browse files Browse the repository at this point in the history
Summary:
TypeScript [supports specifying different getter and setter types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-1.html#unrelated-types-for-getters-and-setters) since 5.1, so I've updated the type generation logic around `updatable` and `assignable` to  work with TypeScript.

I've also updated the notice on the website and made the documentation around type-safe updaters available externally (if this is not wanted, just revert 33612d2).

Companion PR for `types/relay-runtime`: DefinitelyTyped/DefinitelyTyped#66013 (Merged)

Closes #4212

Pull Request resolved: #4370

Reviewed By: alunyov

Differential Revision: D48990085

Pulled By: monicatang

fbshipit-source-id: 51d1af5f4f9b7fc8480d672395544868c24a2c60
  • Loading branch information
tobias-tengler authored and facebook-github-bot committed Oct 6, 2023
1 parent 16910c9 commit 9c7b92a
Show file tree
Hide file tree
Showing 34 changed files with 786 additions and 55 deletions.
20 changes: 16 additions & 4 deletions compiler/crates/relay-typegen/src/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,22 @@ impl TypeScriptPrinter {
self.write(&key_value_pair.value)?;
writeln!(&mut self.result, ";")?;
}
Prop::GetterSetterPair(_) => {
panic!(
"Getters and setters with different types are not implemented in typescript. See https://github.com/microsoft/TypeScript/issues/43662"
);
Prop::GetterSetterPair(getter_setter_pair) => {
// Write the getter
self.write_indentation()?;
write!(&mut self.result, "get ")?;
self.write(&AST::Identifier(getter_setter_pair.key))?;
write!(&mut self.result, "(): ")?;
self.write(&getter_setter_pair.getter_return_value)?;
writeln!(&mut self.result, ";")?;

// Write the setter
self.write_indentation()?;
write!(&mut self.result, "set ")?;
self.write(&AST::Identifier(getter_setter_pair.key))?;
write!(&mut self.result, "(value: ")?;
self.write(&getter_setter_pair.setter_parameter)?;
writeln!(&mut self.result, ");")?;
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions compiler/crates/relay-typegen/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1543,7 +1543,14 @@ fn make_prop(
if linked_field.node_type.is_list() {
AST::RawType(intern!("[]"))
} else {
AST::RawType(intern!("null | void"))
match typegen_context.project_config.typegen_config.language {
TypegenLanguage::Flow | TypegenLanguage::JavaScript => {
AST::RawType(intern!("null | void"))
}
TypegenLanguage::TypeScript => {
AST::RawType(intern!("null | undefined"))
}
}
}
} else {
let setter_parameter = AST::Union(
Expand All @@ -1564,10 +1571,10 @@ fn make_prop(
read_only: true,
optional: false,
});
let assignable_fragment_spread_ref= Prop::KeyValuePair(KeyValuePairProp {
let assignable_fragment_spread_ref = Prop::KeyValuePair(KeyValuePairProp {
key: *KEY_FRAGMENT_SPREADS,
value: AST::FragmentReferenceType(
fragment_spread.fragment_name.0,
value: AST::FragmentReference(
SortedStringKeyList::new(vec![fragment_spread.fragment_name.0]),
),
read_only: true,
optional: false,
Expand Down
42 changes: 31 additions & 11 deletions compiler/crates/relay-typegen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -843,13 +843,23 @@ fn write_abstract_validator_function(
writer.write(&return_type)?;
write!(
writer,
"{} {{\n return value.{} != null ? (value{}: ",
"{} {{\n return value.{} != null ? ",
&close_comment,
abstract_fragment_spread_marker.lookup(),
open_comment
)?;
writer.write(&AST::Any)?;
write!(writer, "{}) : false;\n}}", &close_comment)?;

match language {
TypegenLanguage::Flow | TypegenLanguage::JavaScript => {
write!(writer, "(value{}: ", &open_comment)?;
writer.write(&AST::Any)?;
write!(writer, "{}) ", &close_comment)?;
}
TypegenLanguage::TypeScript => {
write!(writer, "value ")?;
}
}

write!(writer, ": false;\n}}")?;

Ok(())
}
Expand Down Expand Up @@ -916,8 +926,8 @@ fn write_concrete_validator_function(
AST::RawType(intern!("false")),
]));

let (open_comment, close_comment) = match typegen_context.project_config.typegen_config.language
{
let typegen_language = typegen_context.project_config.typegen_config.language;
let (open_comment, close_comment) = match typegen_language {
TypegenLanguage::Flow | TypegenLanguage::JavaScript => ("/*", "*/"),
TypegenLanguage::TypeScript => ("", ""),
};
Expand All @@ -932,14 +942,24 @@ fn write_concrete_validator_function(
writer.write(&return_type)?;
write!(
writer,
"{} {{\n return value.{} === '{}' ? (value{}: ",
"{} {{\n return value.{} === '{}' ? ",
&close_comment,
KEY_TYPENAME.lookup(),
concrete_typename.lookup(),
open_comment
concrete_typename.lookup()
)?;
writer.write(&AST::Any)?;
write!(writer, "{}) : false;\n}}", &close_comment)?;

match typegen_language {
TypegenLanguage::Flow | TypegenLanguage::JavaScript => {
write!(writer, "(value{}: ", &open_comment)?;
writer.write(&AST::Any)?;
write!(writer, "{}) ", &close_comment)?;
}
TypegenLanguage::TypeScript => {
write!(writer, "value ")?;
}
}

write!(writer, ": false;\n}}")?;

Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
==================================== INPUT ====================================
query Foo {
viewer {
...Assignable_viewer
}
}

fragment Assignable_viewer on Viewer @assignable {
__typename
}
==================================== OUTPUT ===================================
import { FragmentRefs } from "relay-runtime";
export type Foo$variables = {};
export type Foo$data = {
readonly viewer: {
readonly __typename: "Viewer";
readonly __id: string;
readonly " $fragmentSpreads": FragmentRefs<"Assignable_viewer">;
} | null;
};
export type Foo = {
response: Foo$data;
variables: Foo$variables;
};
-------------------------------------------------------------------------------
import { FragmentRefs } from "relay-runtime";
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
query Foo {
viewer {
...Assignable_viewer
}
}

fragment Assignable_viewer on Viewer @assignable {
__typename
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
==================================== INPUT ====================================
query updatableFragmentSpreadAndRegularSpreadQuery {
me {
...updatableFragmentSpreadAndRegularSpread_updatable_user
...updatableFragmentSpreadAndRegularSpread_user
}
}

fragment updatableFragmentSpreadAndRegularSpread_updatable_user on User @updatable {
firstName
}

fragment updatableFragmentSpreadAndRegularSpread_user on User {
firstName
}
==================================== OUTPUT ===================================
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpreadAndRegularSpreadQuery$variables = {};
export type updatableFragmentSpreadAndRegularSpreadQuery$data = {
readonly me: {
readonly $updatableFragmentSpreads: FragmentRefs<"updatableFragmentSpreadAndRegularSpread_updatable_user">;
readonly " $fragmentSpreads": FragmentRefs<"updatableFragmentSpreadAndRegularSpread_user">;
} | null;
};
export type updatableFragmentSpreadAndRegularSpreadQuery = {
response: updatableFragmentSpreadAndRegularSpreadQuery$data;
variables: updatableFragmentSpreadAndRegularSpreadQuery$variables;
};
-------------------------------------------------------------------------------
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpreadAndRegularSpread_updatable_user$data = {
firstName: string | null;
readonly " $fragmentType": "updatableFragmentSpreadAndRegularSpread_updatable_user";
};
export type updatableFragmentSpreadAndRegularSpread_updatable_user$key = {
readonly " $data"?: updatableFragmentSpreadAndRegularSpread_updatable_user$data;
readonly $updatableFragmentSpreads: FragmentRefs<"updatableFragmentSpreadAndRegularSpread_updatable_user">;
};
-------------------------------------------------------------------------------
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpreadAndRegularSpread_user$data = {
readonly firstName: string | null;
readonly " $fragmentType": "updatableFragmentSpreadAndRegularSpread_user";
};
export type updatableFragmentSpreadAndRegularSpread_user$key = {
readonly " $data"?: updatableFragmentSpreadAndRegularSpread_user$data;
readonly " $fragmentSpreads": FragmentRefs<"updatableFragmentSpreadAndRegularSpread_user">;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
query updatableFragmentSpreadAndRegularSpreadQuery {
me {
...updatableFragmentSpreadAndRegularSpread_updatable_user
...updatableFragmentSpreadAndRegularSpread_user
}
}

fragment updatableFragmentSpreadAndRegularSpread_updatable_user on User @updatable {
firstName
}

fragment updatableFragmentSpreadAndRegularSpread_user on User {
firstName
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
==================================== INPUT ====================================
query updatableFragmentSpreadAndRegularSpreadQuery {
me {
...updatableFragmentSpreadAndRegularSpread_updatable_user
...updatableFragmentSpreadAndRegularSpread_2_updatable_user
}
}

fragment updatableFragmentSpreadAndRegularSpread_updatable_user on User @updatable {
firstName
}

fragment updatableFragmentSpreadAndRegularSpread_2_updatable_user on User @updatable {
firstName
}
==================================== OUTPUT ===================================
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpreadAndRegularSpreadQuery$variables = {};
export type updatableFragmentSpreadAndRegularSpreadQuery$data = {
readonly me: {
readonly $updatableFragmentSpreads: FragmentRefs<"updatableFragmentSpreadAndRegularSpread_2_updatable_user" | "updatableFragmentSpreadAndRegularSpread_updatable_user">;
} | null;
};
export type updatableFragmentSpreadAndRegularSpreadQuery = {
response: updatableFragmentSpreadAndRegularSpreadQuery$data;
variables: updatableFragmentSpreadAndRegularSpreadQuery$variables;
};
-------------------------------------------------------------------------------
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpreadAndRegularSpread_2_updatable_user$data = {
firstName: string | null;
readonly " $fragmentType": "updatableFragmentSpreadAndRegularSpread_2_updatable_user";
};
export type updatableFragmentSpreadAndRegularSpread_2_updatable_user$key = {
readonly " $data"?: updatableFragmentSpreadAndRegularSpread_2_updatable_user$data;
readonly $updatableFragmentSpreads: FragmentRefs<"updatableFragmentSpreadAndRegularSpread_2_updatable_user">;
};
-------------------------------------------------------------------------------
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpreadAndRegularSpread_updatable_user$data = {
firstName: string | null;
readonly " $fragmentType": "updatableFragmentSpreadAndRegularSpread_updatable_user";
};
export type updatableFragmentSpreadAndRegularSpread_updatable_user$key = {
readonly " $data"?: updatableFragmentSpreadAndRegularSpread_updatable_user$data;
readonly $updatableFragmentSpreads: FragmentRefs<"updatableFragmentSpreadAndRegularSpread_updatable_user">;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
query updatableFragmentSpreadAndRegularSpreadQuery {
me {
...updatableFragmentSpreadAndRegularSpread_updatable_user
...updatableFragmentSpreadAndRegularSpread_2_updatable_user
}
}

fragment updatableFragmentSpreadAndRegularSpread_updatable_user on User @updatable {
firstName
}

fragment updatableFragmentSpreadAndRegularSpread_2_updatable_user on User @updatable {
firstName
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
==================================== INPUT ====================================
query updatableFragmentSpreadQuery {
me {
...updatableFragmentSpread_updatable_user
}
}

fragment updatableFragmentSpread_updatable_user on User @updatable {
firstName
}
==================================== OUTPUT ===================================
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpreadQuery$variables = {};
export type updatableFragmentSpreadQuery$data = {
readonly me: {
readonly $updatableFragmentSpreads: FragmentRefs<"updatableFragmentSpread_updatable_user">;
} | null;
};
export type updatableFragmentSpreadQuery = {
response: updatableFragmentSpreadQuery$data;
variables: updatableFragmentSpreadQuery$variables;
};
-------------------------------------------------------------------------------
import { FragmentRefs } from "relay-runtime";
export type updatableFragmentSpread_updatable_user$data = {
firstName: string | null;
readonly " $fragmentType": "updatableFragmentSpread_updatable_user";
};
export type updatableFragmentSpread_updatable_user$key = {
readonly " $data"?: updatableFragmentSpread_updatable_user$data;
readonly $updatableFragmentSpreads: FragmentRefs<"updatableFragmentSpread_updatable_user">;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
query updatableFragmentSpreadQuery {
me {
...updatableFragmentSpread_updatable_user
}
}

fragment updatableFragmentSpread_updatable_user on User @updatable {
firstName
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
==================================== INPUT ====================================
query UpdatableQuery @updatable {
node(id: 4) {
... on User {
__typename
parents {
...Assignable_user
}
}
}
}

fragment Assignable_user on User @assignable {
__typename
}
==================================== OUTPUT ===================================
import { FragmentRefs } from "relay-runtime";
export type UpdatableQuery$variables = {};
export type UpdatableQuery$data = {
get node(): {
readonly __typename: "User";
get parents(): ReadonlyArray<{}>;
set parents(value: ReadonlyArray<{
readonly __typename: "User";
readonly __id: string;
readonly " $fragmentSpreads": FragmentRefs<"Assignable_user">;
}>);
} | {
// This will never be '%other', but we need some
// value in case none of the concrete values match.
readonly __typename: "%other";
} | null;
set node(value: null | undefined);
};
export type UpdatableQuery = {
response: UpdatableQuery$data;
variables: UpdatableQuery$variables;
};
-------------------------------------------------------------------------------
import { FragmentRefs } from "relay-runtime";
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
query UpdatableQuery @updatable {
node(id: 4) {
... on User {
__typename
parents {
...Assignable_user
}
}
}
}

fragment Assignable_user on User @assignable {
__typename
}
Loading

0 comments on commit 9c7b92a

Please sign in to comment.