Skip to content

Commit ff2133b

Browse files
authored
Merge pull request #8863 from marmelab/ts-record-reference-fields
Add TS support to fields
2 parents fd141cd + b914e0e commit ff2133b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+647
-369
lines changed

docs/Fields.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,4 +597,39 @@ You can find components for react-admin in third-party repositories.
597597

598598
- [OoDeLally/react-admin-clipboard-list-field](https://github.com/OoDeLally/react-admin-clipboard-list-field): a quick and customizable copy-to-clipboard field.
599599
- [MrHertal/react-admin-json-view](https://github.com/MrHertal/react-admin-json-view): JSON field and input for react-admin.
600-
- [alexgschwend/react-admin-color-picker](https://github.com/alexgschwend/react-admin-color-picker): a color field
600+
- [alexgschwend/react-admin-color-picker](https://github.com/alexgschwend/react-admin-color-picker): a color field
601+
602+
## TypeScript
603+
604+
All field components accept a generic type that describes the record. This lets TypeScript validate that the `source` prop targets an actual field of the record:
605+
606+
```tsx
607+
import * as React from "react";
608+
import { Show, SimpleShowLayout, TextField, DateField, RichTextField } from 'react-admin';
609+
610+
// Note that you shouldn't extend RaRecord for this to work
611+
type Post = {
612+
id: number;
613+
title: string;
614+
teaser: string;
615+
body: string;
616+
published_at: string;
617+
}
618+
619+
export const PostShow = () => (
620+
<Show>
621+
<SimpleShowLayout>
622+
<TextField<Post> source="title" />
623+
<TextField<Post> source="teaser" />
624+
{/* Here TS will show an error because a teasr field does not exist */}
625+
<TextField<Post> source="teasr" />
626+
<RichTextField<Post> source="body" />
627+
<DateField<Post> label="Publication date" source="published_at" />
628+
</SimpleShowLayout>
629+
</Show>
630+
);
631+
```
632+
633+
**Limitation**: You must not extend `RaRecord` for this to work. This is because `RaRecord` extends `Record<string, any>` and TypeScript would not be able to infer your types properties.
634+
635+
Specifying the record type will also allow your IDE to provide auto-completion for both the `source` and `sortBy` prop. Note that the `sortBy` prop also accepts any string.

docs/js/prism.js

Lines changed: 11 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/crm/src/companies/LogoField.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import { useRecordContext } from 'react-admin';
33
import { Box } from '@mui/material';
44

5-
import { Company, Contact } from '../types';
5+
import { Company } from '../types';
66

77
const sizeInPixel = {
88
medium: 42,
@@ -14,7 +14,7 @@ export const LogoField = ({
1414
}: {
1515
size?: 'small' | 'medium';
1616
}) => {
17-
const record = useRecordContext<Company | Contact>();
17+
const record = useRecordContext<Company>();
1818
if (!record) return null;
1919
return (
2020
<Box

examples/crm/src/contacts/TagsListEdit.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ export const TagsListEdit = () => {
6161
};
6262

6363
const handleDeleteTag = (id: Identifier) => {
64-
const tags: Identifier[] = record.tags.filter(
65-
(tagId: Identifier) => tagId !== id
66-
);
64+
const tags = record.tags.filter(tagId => tagId !== id);
6765
update('contacts', {
6866
id: record.id,
6967
data: { tags },
@@ -72,7 +70,7 @@ export const TagsListEdit = () => {
7270
};
7371

7472
const handleAddTag = (id: Identifier) => {
75-
const tags: Identifier[] = [...record.tags, id];
73+
const tags = [...record.tags, id];
7674
update('contacts', {
7775
id: record.id,
7876
data: { tags },
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { RaRecord } from 'react-admin';
2-
import { Company, Contact, ContactNote, Deal, Tag } from '../types';
2+
import { Company, Contact, ContactNote, Deal, Sale, Tag } from '../types';
33

44
export interface Db {
55
companies: Company[];
66
contacts: Contact[];
77
contactNotes: ContactNote[];
88
deals: Deal[];
99
dealNotes: RaRecord[];
10-
sales: RaRecord[];
10+
sales: Sale[];
1111
tags: Tag[];
1212
tasks: RaRecord[];
1313
}

examples/crm/src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RaRecord, Identifier } from 'react-admin';
1+
import { Identifier, RaRecord } from 'react-admin';
22

33
export interface Sale extends RaRecord {
44
first_name: string;
@@ -38,6 +38,8 @@ export interface Contact extends RaRecord {
3838
gender: string;
3939
sales_id: Identifier;
4040
nb_notes: number;
41+
status: string;
42+
background: string;
4143
}
4244

4345
export interface ContactNote extends RaRecord {
@@ -59,6 +61,7 @@ export interface Deal extends RaRecord {
5961
amount: number;
6062
created_at: string;
6163
updated_at: string;
64+
start_at: string;
6265
sales_id: Identifier;
6366
index: number;
6467
nb_notes: number;

examples/demo/src/products/ProductReferenceField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface Props {
77

88
const ProductReferenceField = (
99
props: Props &
10-
Omit<Omit<ReferenceFieldProps, 'source'>, 'reference' | 'children'>
10+
Omit<ReferenceFieldProps, 'source' | 'reference' | 'children'>
1111
) => (
1212
<ReferenceField
1313
label="Product"

examples/demo/src/reviews/ReviewItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import {
1616
} from 'react-admin';
1717

1818
import AvatarField from '../visitors/AvatarField';
19-
import { Customer } from './../types';
19+
import { Customer, Review } from './../types';
2020

2121
export const ReviewItem = () => {
22-
const record = useRecordContext();
22+
const record = useRecordContext<Review>();
2323
const createPath = useCreatePath();
2424
if (!record) {
2525
return null;

examples/demo/src/types.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RaRecord, Identifier } from 'react-admin';
1+
import { Identifier, RaRecord } from 'react-admin';
22

33
export type ThemeName = 'light' | 'dark';
44

@@ -35,6 +35,7 @@ export interface Customer extends RaRecord {
3535
groups: string[];
3636
nb_commands: number;
3737
total_spent: number;
38+
email: string;
3839
}
3940

4041
export type OrderStatus = 'ordered' | 'delivered' | 'cancelled';
@@ -44,14 +45,22 @@ export interface Order extends RaRecord {
4445
basket: BasketItem[];
4546
date: Date;
4647
total: number;
48+
total_ex_taxes: number;
49+
delivery_fees: number;
50+
tax_rate: number;
51+
taxes: number;
52+
customer_id: Identifier;
53+
reference: string;
4754
}
4855

49-
export interface BasketItem {
56+
export type BasketItem = {
5057
product_id: Identifier;
5158
quantity: number;
52-
}
59+
};
5360

54-
export interface Invoice extends RaRecord {}
61+
export interface Invoice extends RaRecord {
62+
date: Date;
63+
}
5564

5665
export type ReviewStatus = 'accepted' | 'pending' | 'rejected';
5766

@@ -60,6 +69,7 @@ export interface Review extends RaRecord {
6069
status: ReviewStatus;
6170
customer_id: Identifier;
6271
product_id: Identifier;
72+
comment: string;
6373
}
6474

6575
declare global {

examples/demo/src/visitors/FullNameField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const FullNameField = (props: Props) => {
3838
};
3939

4040
FullNameField.defaultProps = {
41-
source: 'last_name',
41+
source: 'last_name' as const,
4242
label: 'resources.customers.fields.name',
4343
};
4444

0 commit comments

Comments
 (0)