【问题标题】:TypeScript: Require one of two destructured paramsTypeScript:需要两个解构参数之一
【发布时间】:2020-05-23 14:52:23
【问题描述】:

在创建需要设置两个可能属性之一的接口时,类型联合很有帮助:

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

但是,当使用该接口来解构函数参数时,事情突然变得很糟糕:

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,
                                     ~~~~~~

我们怎样才能做到这一点?

【问题讨论】:

    标签: typescript optional-parameters destructuring required-field


    【解决方案1】:

    TypeScript 编译器不能以这种方式分解。当您使用类型的联合时,它会尝试查找您所看到的公共字段。因为联合意味着这个或这个对象。不是这个 AND 这个对象。

    您可能应该重写您的接口。在this article 你可以找到一个可以启发你的例子。

    【讨论】:

      【解决方案2】:

      最终使用some type guards with rest parameters 使可选参数不明确。

      // 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;
      }
      

      【讨论】:

        猜你喜欢
        • 2012-06-24
        • 2021-12-18
        • 2021-09-10
        • 1970-01-01
        • 2020-07-18
        • 2015-07-15
        • 2018-10-21
        • 1970-01-01
        相关资源
        最近更新 更多