aws-amplify / aws-amplify/amplify-codegen
Generated Typescript types are inconsistent
- Dominant language
- TypeScript
- Stars
- 59
- Forks
- 64
- PR merge metrics
- No merged PRs in 30d
Description
### Before opening, please confirm:
- [X] I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
- [X] I have [searched for duplicate or closed issues](https://github.com/aws-amplify/amplify-cli/issues?q=is%3Aissue+).
- [X] I have read the guide for [submitting bug reports](https://github.com/aws-amplify/amplify-cli/blob/master/CONTRIBUTING.md#bug-reports).
- [X] I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
- [X] I have removed any sensitive information from my code snippets and submission.
### How did you install the Amplify CLI?
yarn
### If applicable, what version of Node.js are you using?
16.13.0
### Amplify CLI Version
7.6.20
### What operating system are you using?
mac
### Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.
No
### Amplify Categories
api
### Amplify Commands
codegen
### Describe the bug
Using GraphQL and Opensearch, Amplify defines the following types in src/API.ts:
```
export type SearchableInvoiceConnection = {
__typename: "SearchableInvoiceConnection",
items: Array,
nextToken?: string | null,
total?: number | null,
aggregateItems: Array,
};
export type SearchableAggregateResult = {
__typename: "SearchableAggregateResult",
name: string,
result?: SearchableAggregateGenericResult | null,
};
export type SearchableAggregateGenericResult = SearchableAggregateScalarResult | SearchableAggregateBucketResult
export type SearchableAggregateScalarResult = {
__typename: "SearchableAggregateScalarResult",
value: number,
};
export type SearchableAggregateBucketResult = {
__typename: "SearchableAggregateBucketResult",
buckets?: Array | null,
};
export type SearchableAggregateBucketResultItem = {
__typename: "SearchableAggregateBucketResultItem",
key: string,
doc_count: number,
};
```
Based on these, I should be able to write a type guard that I can use when iterating over the aggregates returned by OpenSearch to distinguish bucket results from scalar results:
```
export function isSearchableAggregateBucketResult(
item: SearchableAggregateGenericResult | null | undefined
): item is SearchableAggregateBucketResult {
return item !== null && item !== undefined && "buckets" in item;
}
```
However, this code produces an error:
```
TS2345: Argument of type '{ __typename: "SearchableAggregateScalarResult"; value: number; } | { __typename: "SearchableAggregateBucketResult"; buckets?: ({ __typename: string; key: string; doc_count: number; } | null)[] | null | undefined; } | null' is not assignable to parameter of type 'SearchableAggregateGenericResult | null | undefined'.
```
The reason for this is that the type of the OpenSearch results doesn't use any of the above types. Instead, it's typed this way:
```
export type SearchInvoicesQuery = {
searchInvoices?: {
__typename: "SearchableInvoiceConnection",
items: Array< {
__typename: "Invoice",
id: string,
supplier_paidol_id: string,
supplier_paidol_name: string,
accounts_receivable_name: string,
date_sent: string,
date_due: string,
createdAt: string,
sentAt?: string | null,
status: InvoiceStatus,
updatedAt: string,
} | null >,
nextToken?: string | null,
total?: number | null,
aggregateItems: Array< {
__typename: "SearchableAggregateResult",
name: string,
result: ( {
__typename: "SearchableAggregateScalarResult",
value: number,
} | {
__typename: "SearchableAggregateBucketResult",
buckets?: Array< {
__typename: string,
key: string,
doc_count: number,
} | null > | null,
}
) | null,
} | null >,
} | null,
};
```
Although the typing references `__typename: "SearchableInvoiceConnection"`, etc., the types are **not** actually composed here. This means that the type guard needs to be written like this:
```
export function isSearchableAggregateBucketResult(
item:
| { __typename: "SearchableAggregateScalarResult"; value: number }
| {
__typename: "SearchableAggregateBucketResult";
buckets?:
| ({ __typename: string; key: string; doc_count: number } | null)[]
| null
| undefined;
}
| null
| undefined
): item is SearchableAggregateBucketResult {
return item !== null && item !== undefined && "buckets" in item;
}
```
### Expected behavior
API.ts should either use the types it defines, or those types should not be exposed to developers. They are not usable in their current form.
### Reproduction steps
Define any `@model @searchable` in GraphQL and consume the results in Typescript.
### GraphQL schema(s)
```graphql
type Invoice
@model(mutations: null, subscriptions: null)
@auth(rules: [{ allow: public, provider: iam, operations: [read] }])
@searchable {
id: ID!
supplier_paidol_id: ID!
supplier_paidol: Paidol @hasOne(fields: ["supplier_paidol_id"])
supplier_paidol_name: String!
accounts_receivable_name: String!
date_sent: AWSDateTime!
date_due: AWSDateTime!
balance_due: Currency
createdAt: AWSDateTime!
sentAt: AWSDateTime
status: InvoiceStatus!
items: [InvoiceItem] @hasMany(indexName: "byInvoice", fields: ["id"])
}
```
### Log output
```
ERROR in src/store/invoicesSlice.ts:79:49
TS2345: Argument of type '{ __typename: "SearchableAggregateScalarResult"; value: number; } | { __typename: "SearchableAggregateBucketResult"; buckets?: ({ __typename: string; key: string; doc_count: number; } | null)[] | null | undefined; } | null' is not assignable to parameter of type 'SearchableAggregateGenericResult | null | undefined'.
Type '{ __typename: "SearchableAggregateBucketResult"; buckets?: ({ __typename: string; key: string; doc_count: number; } | null)[] | null | undefined; }' is not assignable to type 'SearchableAggregateGenericResult | null | undefined'.
Type '{ __typename: "SearchableAggregateBucketResult"; buckets?: ({ __typename: string; key: string; doc_count: number; } | null)[] | null | undefined; }' is not assignable to type 'SearchableAggregateBucketResult'.
Types of property 'buckets' are incompatible.
Type '({ __typename: string; key: string; doc_count: number; } | null)[] | null | undefined' is not assignable to type '(SearchableAggregateBucketResultItem | null)[] | null | undefined'.
Type '({ __typename: string; key: string; doc_count: number; } | null)[]' is not assignable to type '(SearchableAggregateBucketResultItem | null)[]'.
Type '{ __typename: string; key: string; doc_count: number; } | null' is not assignable to type 'SearchableAggregateBucketResultItem | null'.
Type '{ __typename: string; key: string; doc_count: number; }' is not assignable to type 'SearchableAggregateBucketResultItem'.
Types of property '__typename' are incompatible.
Type 'string' is not assignable to type '"SearchableAggregateBucketResultItem"'.
77 | .filter(isNotNull)
78 | .reduce((accumulator, current) => {
> 79 | if (isSearchableAggregateBucketResult(current.result)) {
| ^^^^^^^^^^^^^^
80 | accumulator[current.name] = (current.result.buckets ?? [])
81 | .filter(isNotNull)
82 | .map((item) => item.key)
```
### Additional information
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.