-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TypeScript] Fix FunctionField render function type expects a nullable record #8963
[TypeScript] Fix FunctionField render function type expects a nullable record #8963
Conversation
@@ -133,9 +133,7 @@ const Contact = ({ note }: any) => ( | |||
> | |||
<FunctionField<ContactType> | |||
variant="body2" | |||
render={(contact?: ContactType) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ContactType
is already set by line 134
export type FunctionFieldRenderer< | ||
RecordType extends Record<string, unknown> = Record<string, any> | ||
> = (record: RecordType, source?: string) => React.ReactNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was unsure on what to name this 🤔. It may be helpful to further abstract this into a type that’s shared by <WithRecord>
Great PR, thanks! I think we can safely merge it to Would you mind rebasing the PR on master? |
964310e
to
0d923ad
Compare
0d923ad
to
de255ad
Compare
export const WithRecord = <RecordType extends RaRecord>({ | ||
export const WithRecord = <RecordType extends Record<string, unknown> = any>({ | ||
render, | ||
}: WithRecordProps<RecordType>) => { | ||
const record = useRecordContext<RecordType>(); | ||
return record ? render(record) : null; | ||
}; | ||
|
||
export interface WithRecordProps<RecordType extends RaRecord> { | ||
render: (record: RecordType) => ReactElement; | ||
export interface WithRecordProps< | ||
RecordType extends Record<string, unknown> = any | ||
> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated these to match #8964
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fzaninotto @slax57 it might be helpful to create a “strict” RaRecord
type that we can share/extend from? Could name it StrictRaRecord
, or TypedRaRecord
, etc
e.g.
// ra-core/src/types.ts
export interface StrictRaRecord<IdentifierType extends Identifier = Identifier> {
id: IdentifierType;
}
export interface RaRecord<IdentifierType extends Identifier = Identifier> extends StrictRaRecord {
[key: string]: any;
}
// in other files…
export interface WithRecordProps<RecordType extends StrictRaRecord = RaRecord> { … }
export interface FunctionFieldProps<RecordType extends StrictRaRecord = RaRecord> { … }
// When typing field components…
interface Post extends StrictRaRecord<number> {
title: string;
teaser: string;
body: string;
published_at: string;
}
export const PostShow = () => (
<Show>
<SimpleShowLayout>
<TextField<Post> source="title" />
<TextField<Post> source="teaser" />
{/* Here TS will show an error because a teasr field does not exist */}
<TextField<Post> source="teasr" />
<RichTextField<Post> source="body" />
<DateField<Post> label="Publication date" source="published_at" />
</SimpleShowLayout>
</Show>
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure of the added value (extending a StrictRaRecord vs adding an Identifier by hand is about the same). Besides, it's a more general reflexion about struct null checks that we're working on in the long term.
export type RenderRecordFunction< | ||
RecordType extends Record<string, unknown> = any | ||
> = (record: RecordType, source?: string) => ReactNode; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if there’s a more appropriate place to put this. Named to match the existing RenderResourcesFunction
:
react-admin/packages/ra-core/src/types.ts
Line 303 in b007f35
export type RenderResourcesFunction = ( |
`record` is guaranteed to not be `undefined`, as there’s a falsey check on line 30. It also must return a viable ReactNode that can be rendered.
Matches changes implemented in marmelab#8964
b007f35
to
ddde0c5
Compare
@fzaninotto rebased, and created a shared |
Thanks! |
When using Typescript, the
FunctionField
render prop currently requires redundant null checks, as it claimsrecord
can beundefined
. However it’s guaranteed not to, thanks to the null check on line 30:react-admin/packages/ra-ui-materialui/src/field/FunctionField.tsx
Lines 30 to 39 in bbec8d9
This same guarantee applies to
<WithRecord>
, which is documented here:react-admin/docs/WithRecord.md
Line 26 in 964310e
This PR updates the
render
prop type to clarify thatrecord
will be defined, eliminating the need for redundant null checks:The PR also:
RenderResourcesFunction
type, that can be used when creating custom render methodsrender
method must return a validReactNode
, notany
.FunctionField
examples)