#javascript #node.js #typescript #types #typescript-generics
#javascript #node.js #typescript #типы #typescript-generics
Вопрос:
Я создаю пользовательский класс ошибок, подобный этому:
import { OperationalError } from './baseErrors';
type ErrorProps = {
message: string;
paymentProvider: 'stripe' | 'braintree';
failedChargeData: {
errorMessage: string;
stripePaymentIntentId?: string;
braintreeChargeId?: string;
eventType: string;
};
};
export class FailedPaymentError extends OperationalError {
constructor({ message, paymentProvider, failedChargeData }: ErrorProps) {
const properties = { failedChargeData, paymentProvider };
super(message, properties, 400);
}
}
Однако я хочу улучшить это.
Я хочу написать какой — нибудь условный тип , чтобы if paymentProvider === 'stripe'
, then ErrorProps
требовал a stripePaymentIntentId
. Принимая во внимание, что если paymentProvider === 'braintree'
, то braintreeChargeId
требуется a.
Я думаю, мне нужно сделать что-то подобное?
import { OperationalError } from './baseErrors';
type PaymentProvider = 'stripe' | 'braintree';
type ErrorProps<T> = {
message: string;
paymentProvider: T;
failedChargeData: T extends 'stripe'
? {
errorMessage: string;
stripePaymentIntentId: string;
eventType: string;
}
: {
errorMessage: string;
braintreeChargeId: string;
eventType: string;
};
};
export class FailedPaymentError extends OperationalError {
constructor({
message,
paymentProvider,
failedChargeData,
}: ErrorProps<PaymentProvider>) {
const properties = { failedChargeData, paymentProvider };
super(message, properties, 400);
}
}
Таким образом, я могу затем выдать ошибку следующим образом:
const failedChargeData = {
errorMessage: message,
stripePaymentIntentId: e.type amp;amp; e.requestId,
eventType: e.type || 'unexpectedFailedPayment',
};
throw new FailedPaymentError({
message,
paymentProvider: 'stripe',
failedChargeData,
});
Но это не совсем работает. TS не жалуется, если я прохожу через неправильного поставщика платежей. Кроме того, он не особенно СУХОЙ.
Это ДЕЙСТВИТЕЛЬНО работает, но несколько строк кажутся излишне дублированными:
type ErrorProps =
| {
message: string;
paymentProvider: 'stripe';
failedChargeData: {
errorMessage: string;
stripePaymentIntentId: string;
eventType: string;
};
}
| {
message: string;
paymentProvider: 'braintree';
failedChargeData: {
errorMessage: string;
braintreeChargeId: string;
eventType: string;
};
};
Как я могу это сделать? Спасибо
Ответ №1:
Различаемое объединение в вашем последнем примере абсолютно правильное.
Вы можете удалить его, извлекая различаемые типы из базового интерфейса.
type MapErrorProps<T, K> = {
message: string;
paymentProvider: T;
failedChargeData: {
errorMessage: string;
eventType: string;
} amp; K;
};
// Map each type to a required data interface
type ErrorProps = MapErrorProps<"stripe", { stripePaymentIntentId: string }>
| MapErrorProps<"braintree", { braintreeChargeId: string }>;
| MapErrorProps<"iceCream", { flavor: string }>;