Stripe.Event.Data is too untyped
See original GitHub issueCurrently, when doing stripe.webhooks.constructEvent
or stripe.webhooks.constructEventAsync
, the event data (which is what we want) is fully untyped. This forces lots of casting, although the type of the event correlates directly with specific stripe objects. We can actually type Stripe.Event
so that narrowing event.type
(ie, event.type === "payment_intent.succeeded"
) also narrows the event.data.object
and event.data.previous_attributes
(for the previous example, Stripe.PaymentIntent
).
I’ve been using this in my codebase and it’s helped tremendously. I haven’t been able to use a typescript declaration to override Stripe.Event
since declarations union with existing definitions, which keeps Stripe.Event.Data.Object
as any
no matter what. Can we have the official types use this?
import type Stripe from "stripe";
type WebhookObjects = {
// https://stripe.com/docs/api/events/types
[EventName in Exclude<
Stripe.WebhookEndpointCreateParams.EnabledEvent,
"*"
>]: EventName extends `account.application.${string}`
? Stripe.Application
: EventName extends `account.external_account.${string}`
? Stripe.Card | Stripe.BankAccount
: EventName extends `account.${string}`
? Stripe.Account
: EventName extends `application_fee.refund.${string}`
? Stripe.FeeRefund
: EventName extends `application_fee.${string}`
? Stripe.ApplicationFee
: EventName extends `balance.${string}`
? Stripe.Balance
: EventName extends `billing_portal.configuration.${string}`
? Stripe.BillingPortal.Configuration
: EventName extends `capability.${string}`
? Stripe.Capability
: EventName extends `charge.dispute.${string}`
? Stripe.Dispute
: EventName extends `charge.refund.${string}`
? Stripe.Refund
: EventName extends `charge.${string}`
? Stripe.Charge
: EventName extends `checkout.session.${string}`
? Stripe.Checkout.Session
: EventName extends `coupon.${string}`
? Stripe.Coupon
: EventName extends `credit_note.${string}`
? Stripe.CreditNote
: EventName extends `customer.discount.${string}`
? Stripe.Discount
: EventName extends `customer.source.${string}`
? Stripe.Discount
: EventName extends `customer.subscription.${string}`
? Stripe.Subscription
: EventName extends `customer.tax_id.${string}`
? Stripe.TaxId
: EventName extends `customer.${string}`
? Stripe.Customer
: EventName extends `file.${string}`
? Stripe.File
: EventName extends `identity.verification_session.${string}`
? Stripe.Identity.VerificationSession
: EventName extends `invoice.${string}`
? Stripe.Invoice
: EventName extends `invoiceitem.${string}`
? Stripe.InvoiceItem
: EventName extends `issuing_authorization.${string}`
? Stripe.Issuing.Authorization
: EventName extends `issuing_card.${string}`
? Stripe.Issuing.Card
: EventName extends `issuing_cardholder.${string}`
? Stripe.Issuing.Cardholder
: EventName extends `issuing_dispute.${string}`
? Stripe.Issuing.Dispute
: EventName extends `issuing_transaction.${string}`
? Stripe.Issuing.Transaction
: EventName extends `linked_account.${string}`
? Stripe.AccountLink
: EventName extends `mandate.${string}`
? Stripe.Mandate
: EventName extends `order.${string}`
? Stripe.Order
: EventName extends `order_return.${string}`
? Stripe.OrderReturn
: EventName extends `payment_intent.${string}`
? Stripe.PaymentIntent
: EventName extends `payment_link.${string}`
? Stripe.PaymentLink
: EventName extends `payment_method.${string}`
? Stripe.PaymentMethod
: EventName extends `payout.${string}`
? Stripe.Payout
: EventName extends `person.${string}`
? Stripe.Person
: EventName extends `plan.${string}`
? Stripe.Plan
: EventName extends `price.${string}`
? Stripe.Price
: EventName extends `product.${string}`
? Stripe.Product
: EventName extends `promotion_code.${string}`
? Stripe.PromotionCode
: EventName extends `quote.${string}`
? Stripe.Quote
: EventName extends `radar.early_fraud_warning.${string}`
? Stripe.Radar.EarlyFraudWarning
: EventName extends `recipient.${string}`
? Stripe.Recipient
: EventName extends `reporting.report_run.${string}`
? Stripe.Reporting.ReportRun
: EventName extends `reporting.report_type.${string}`
? Stripe.Reporting.ReportType
: EventName extends `review.${string}`
? Stripe.Review
: EventName extends `setup_intent.${string}`
? Stripe.SetupIntent
: EventName extends `sigma.scheduled_query_run.${string}`
? Stripe.Sigma.ScheduledQueryRun
: EventName extends `sku.${string}`
? Stripe.Sku
: EventName extends `source.transaction.${string}`
? Stripe.SourceTransaction
: EventName extends `source.${string}`
? Stripe.Source
: EventName extends `subscription_schedule.${string}`
? Stripe.SubscriptionSchedule
: EventName extends `tax_rate.${string}`
? Stripe.TaxRate
: EventName extends `topup.${string}`
? Stripe.Topup
: EventName extends `transfer.${string}`
? Stripe.Transfer
: never;
};
export type StripeEvent = Omit<Stripe.Event, "data"> &
{
[EventName in keyof WebhookObjects]: {
// Stripe.Event.Data.Object is fully untyped. This will let us type narrow the type of data.object by type.
data: EventName extends `${string}.updated`
? {
object: WebhookObjects[EventName];
previous_attributes: Partial<WebhookObjects[EventName]>;
}
: {
object: WebhookObjects[EventName];
};
type: EventName;
};
}[keyof WebhookObjects];
Issue Analytics
- State:
- Created a year ago
- Reactions:3
- Comments:6 (3 by maintainers)
That’s fair! I still think some intermediate would be good, event if
Stripe.Event.Data
was just a union of all of those types. Currently there’s no version of narrowing types, the only path is forced casting. I’m wondering if there’s an iterative path that leads to typing this based on all the factors.To keep things keep I’m going to close this as a duplicate. Please track related work in https://github.com/stripe/stripe-node/issues/758.