I needed to add a feature that generated a receipt, based on the stripe payment information, and ran into some issues with the type interface.
The most significant difficulty in my cases was the need to use the as unknown astechnique with an override type, when consuming receipt data — primarily due to a pluralization inconsistency between terminalVerificationResult (expected) and terminalVerificationResults (actual)
Example Code
```tsx
type ReceiptDetailsBase = PaymentIntent.Type['offlineDetails']['cardPresentDetails']['receiptDetails'];
type ReceiptDetailsOverride = Omit & {
terminalVerificationResults: ReceiptDetailsBase['terminalVerificationResult'];
} | undefined;
export function generateOrderReceipt(paymentIntent: PaymentIntent.Type) {
const { paymentMethodDetails } = paymentIntent.charges[0];
const { cardPresentDetails } = paymentMethodDetails;
const receiptDetails = cardPresentDetails?.receipt as unknown as ReceiptDetailsOverride;
...
}
```
Upon investigating further, there appear to be additional type mismatch issues (see below)
Repro
Add console.log(JSON.stringify(paymentIntent)) in payment handler
Submit test payment
Copy log output and set as typed variable like const ExamplePaymentIntent: PaymentIntent.Type = {...}
I think to start with, just ensuring that the types match what's being generated would be an excellent improvement.
Type Annotations?
Another great enhancement would be to add annotations to the types so that they can be picked up via intellisense.
Here's an example where it could be useful:
Our existing receipt interface included an `emv` object and it wasn't immediately obvious how to map these fields
```tsx
emv: {
application_label: ???,
network_label: ???,
mode: ???,
tvr: ???,
arc: ???,
ac: ???,
tsi: ???,
cvm: ???,
aid: ???,
iad: ???,
}
```
Big fan of the decision to prefer verbose property names over abbreviation, but I think this is an area where type annotations could be really useful, as they could include the standard abbreviation for a given field.
Eventually I landed on this (although I think I need to iron out a couple things)
```tsx
emv: {
application_label: receiptDetails?.applicationPreferredName,
network_label: cardPresentDetails?.network,
mode: 'Issuer', // @REVIEW
tvr: receiptDetails?.terminalVerificationResults, // @REVIEW
arc: receiptDetails?.authorizationResponseCode,
ac: receiptDetails?.applicationCryptogram,
tsi: receiptDetails?.transactionStatusInformation,
cvm: receiptDetails?.accountType,
aid: receiptDetails?.dedicatedFileName,
iad: receiptDetails?.applicationCryptogram,
},
```
Discriminated Unions?
I think in a perfect world the PaymentIntent type would be a union of every major permutation of the model, something like:
type PaymentIntent = PaymentIntentInitial | PaymentIntentConfirmed | PaymentIntentCancelled
Note: Since it's aggregating other models the unions, those nested, constituent models would also probably have union types
Ideally with this approach there's a dedicated enum field that can be used for the discriminated union pattern
https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
I think there's a way to include type narrowing via the return type, but it may also be advantageous to have a generic where users can pass the intended permutation as an argument to the type interface.
Consolidation?
I think there's also something to be said for consolidating fields, eg:
type CardPresentDetails = CardPresentDetailsStandard | InteracPresentDetails
It was sometimes difficult to know which selection path to use — and it would be great to be able to rely more decisively on the type signature and remove the need for trial and error.
Please let me know if I can be of any additional help!
Overview
I needed to add a feature that generated a receipt, based on the stripe payment information, and ran into some issues with the type interface.
The most significant difficulty in my cases was the need to use the
as unknown as
technique with an override type, when consuming receipt data — primarily due to a pluralization inconsistency betweenterminalVerificationResult
(expected) andterminalVerificationResults
(actual)Example Code
```tsx type ReceiptDetailsBase = PaymentIntent.Type['offlineDetails']['cardPresentDetails']['receiptDetails']; type ReceiptDetailsOverride = OmitUpon investigating further, there appear to be additional type mismatch issues (see below)
Repro
console.log(JSON.stringify(paymentIntent))
in payment handlerconst ExamplePaymentIntent: PaymentIntent.Type = {...}
Screenshot
![image](https://github.com/user-attachments/assets/60e5543e-bdb9-46dc-b8d9-106d7204eba5)Additional Considerations
There's a lot one could do with type definitions
I think to start with, just ensuring that the types match what's being generated would be an excellent improvement.
Type Annotations?
Another great enhancement would be to add annotations to the types so that they can be picked up via intellisense. Here's an example where it could be useful: Our existing receipt interface included an `emv` object and it wasn't immediately obvious how to map these fields ```tsx emv: { application_label: ???, network_label: ???, mode: ???, tvr: ???, arc: ???, ac: ???, tsi: ???, cvm: ???, aid: ???, iad: ???, } ``` Big fan of the decision to prefer verbose property names over abbreviation, but I think this is an area where type annotations could be really useful, as they could include the standard abbreviation for a given field. Eventually I landed on this (although I think I need to iron out a couple things) ```tsx emv: { application_label: receiptDetails?.applicationPreferredName, network_label: cardPresentDetails?.network, mode: 'Issuer', // @REVIEW tvr: receiptDetails?.terminalVerificationResults, // @REVIEW arc: receiptDetails?.authorizationResponseCode, ac: receiptDetails?.applicationCryptogram, tsi: receiptDetails?.transactionStatusInformation, cvm: receiptDetails?.accountType, aid: receiptDetails?.dedicatedFileName, iad: receiptDetails?.applicationCryptogram, }, ```Discriminated Unions?
I think in a perfect world the PaymentIntent type would be a union of every major permutation of the model, something like: type PaymentIntent = PaymentIntentInitial | PaymentIntentConfirmed | PaymentIntentCancelled Note: Since it's aggregating other models the unions, those nested, constituent models would also probably have union types Ideally with this approach there's a dedicated enum field that can be used for the discriminated union pattern https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions I think there's a way to include type narrowing via the return type, but it may also be advantageous to have a generic where users can pass the intended permutation as an argument to the type interface.Consolidation?
I think there's also something to be said for consolidating fields, eg: type CardPresentDetails = CardPresentDetailsStandard | InteracPresentDetails It was sometimes difficult to know which selection path to use — and it would be great to be able to rely more decisively on the type signature and remove the need for trial and error.Please let me know if I can be of any additional help!
Thanks!!