aws-amplify / aws-amplify/amplify-codegen

Generated Typescript types are inconsistent

Open
#392 2 comments 1 reaction 0 assignees View on GitHub
bug p3 transferred type-gen
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.