TypeScript: требуется один из двух деструктурированных параметров - PullRequest
0 голосов
/ 07 февраля 2020

При создании интерфейса, для которого требуется установить одно из двух возможных свойств, полезно объединение типов:

enum Plans { bronze, silver, gold }
type Plan = keyof typeof Plans;

interface CreateSubscriptionBase {
  /** User's id */
  uid: string;
  /** Plan id */
  planName: Plan;
}

interface CreateSubscriptionPaymentMethod extends CreateSubscriptionBase {
  /** Payment Method id */
  paymentMethod: string;
}

interface CreateSubscriptionSource extends CreateSubscriptionBase {
  /** Source id */
  source: string;
}

export type CreateSubscriptionParams =
    CreateSubscriptionPaymentMethod
  | CreateSubscriptionSource

const sParams = {
  uid: 'foo',
  planName: 'bronze',
  source: 'source',
} as CreateSubscriptionParams

const pParams = {
  uid: 'foo',
  planName: 'bronze',
  paymentMethod: 'paymentMethod',
} as CreateSubscriptionParams

Однако при использовании этого интерфейса для деструкции параметров функции все происходит внезапно go screwy:

function createSubscription({ uid, planName, paymentMethod, source }: CreateSubscriptionParams) {
  return 'hi'
}
example.ts:36:46 - error TS2339: Property 'paymentMethod' does not exist on type 'CreateSubscriptionParams'.

36   { uid, planName, paymentMethod, source }: CreateSubscriptionParams,
                      ~~~~~~~~~~~~~

example.ts:36:61 - error TS2339: Property 'source' does not exist on type 'CreateSubscriptionParams'.

36   { uid, planName, paymentMethod, source }: CreateSubscriptionParams,
                                     ~~~~~~

Как мы можем это осуществить?

Ответы [ 2 ]

0 голосов
/ 10 февраля 2020

Завершено использованием некоторых типов охранников с параметрами покоя , чтобы сделать необязательные параметры двусмысленными.

// let's imagine some app types
interface Customer extends Record<string, any> { }
interface Subscription extends Record<string, any> {}
enum Prices {
  bronze = 30,
  silver = 40,
  gold = 100,
}

type Plan = keyof typeof Prices

interface UidParams {
  uid: string;
}

interface PaymentMethodParams {
  paymentMethod: { id: string }['id'];
}

interface SourceParams {
  source: { id: string }['id'];
}

interface CreateSubscriptionBase {
  planName: Plan;
  coupon?: string;
}

type CreateSubscriptionParams =
  CreateSubscriptionBase & UidParams & (PaymentMethodParams | SourceParams)

const isPaymentMethod = (params:
    PaymentMethodParams
  | SourceParams): params is PaymentMethodParams =>
  !!(params as PaymentMethodParams).paymentMethod &&
  !(params as SourceParams).source;

const catchAttachError =
  (paymentTypeName: keyof PaymentMethodParams | keyof SourceParams) =>
    (error: Error): never => {
      console.error(error);
      throw new Error(`Could not attach ${paymentTypeName} to customer`);
    };

declare function attachPaymentMethod(params: UidParams & PaymentMethodParams): Promise<Customer>
declare function attachSource(params: UidParams & SourceParams): Promise<Customer>

async function attachPaymentType(
  { uid, ...paymentType }: UidParams & (PaymentMethodParams | SourceParams)
): Promise<Customer> {
  return isPaymentMethod(paymentType)
    ? attachPaymentMethod({ uid, ...paymentType }).catch(catchAttachError('paymentMethod'))
    : attachSource({ uid, ...paymentType }).catch(catchAttachError('source'));
}

async function createSubscription({ coupon, planName, uid, ...paymentType }: CreateSubscriptionParams): Promise<Subscription> {
  const { id: customer } = await attachPaymentType({ uid, ...paymentType });
  // ... yadda yadda ... imagine we did it
  const subscription = {} as Subscription;
  return subscription;
}
0 голосов
/ 07 февраля 2020

Компилятор TypeScript не может быть разложен таким образом. Когда вы используете объединение типов, оно пытается найти общие поля, как вы видите. Потому что союз означает это или этот объект. Не это И этот объект.

Возможно, вам следует переписать ваши интерфейсы. В этой статье вы можете найти пример, который может вас вдохновить.

...