【问题标题】:How to properly deal with optional parameter如何正确处理可选参数
【发布时间】:2018-08-03 00:20:53
【问题描述】:

我正在使用 Typescript,我想编写一个带有可选参数的函数。我不确定这是否是正确的地方,因为没有问题,但它是关于编写干净的代码:

public addResource(config?: ResourceConfig): void {
  if (!config) {
    config = {} as ResourceConfig;
  }
  const resource: FormGroup = this.createResource(config); 
  this.control.push(resource);
}

private createResource({ amount, profit, load }: ResourceConfig): FormGroup {
  return new FormGroup({
    amount: new FormControl(amount || 1),
    profit: new FormControl(profit || 1),
    load: new FormControl(load || 1)
  });
}

...

interface ResourceConfig {
  amount: number;
  profit: number;
  load: number;
}

【问题讨论】:

    标签: typescript interface


    【解决方案1】:

    我假设您使用的是 --strictNullChecks 编译器选项,如果您没有使用,那么您应该这样做。


    我认为可选参数本身不是问题,但从您的使用情况来看,您似乎希望传递给addResource() 和createResource() 的ResourceConfig 对象的各个属性可能会丢失。但是这些方法的签名并没有这么说。这造成了一种情况,您必须尝试回避 TypeScript 的类型系统,而不是使用它。

    具体来说,config = {} as ResourceConfig; 行有问题。您实际上使用type assertion 欺骗了编译器,因为空对象{} 不是ResourceConfig。有时对编译器的善意的谎言很有用,特别是如果它们只是临时的(例如,如果在那一行之后你继续在一个最初为空的对象中设置所有属性)。但是在这种情况下,您永远不会填充config 的属性,因此欺骗传递到createResource() 方法...其中amount、profit 和load 变量被编译器认为是@ 987654337@ 但实际上在运行时可能是undefined。您可以通过检查falsiness 的每个值来处理这种可能性,但如果我相信类型系统,这似乎是不必要的。


    有不同的方法来处理这个问题。一种是说如果config 缺少某些属性也没关系。这意味着您实际上拥有Partial<ResourceConfig>,而不是处理ResourceConfig,其中Partial<T> 是标准库中的mapped type,其definition 是

    type Partial<T> = { [P in keyof T]?: T[P] };
    

    所以Partial&lt;ResourceConfig&gt; 等价于

    {
      amount?: number;
      profit?: number;
      load?: number;
    }
    

    使用它,你会得到:

    public addResource(config?: Partial<ResourceConfig>): void {
      if (!config) {
        config = {}; // truth
      }
      const resource: FormGroup = this.createResource(config);
      this.control.push(resource);
    }
    
    private createResource({ amount, profit, load }: Partial<ResourceConfig>): FormGroup {
      return new FormGroup({
        amount: new FormControl(amount || 1),
        profit: new FormControl(profit || 1),
        load: new FormControl(load || 1)
      });
    }
    

    不需要类型断言,编译器知道createResource中的变量是number | undefined,传递给FormControl构造函数的参数也知道是number,因为|| 1 把戏。


    解决此问题的另一种方法是实际要求config 是ResourceConfig | undefined,如果未定义,则为其分配一个有效的ResourceConfig:

    public addResource(config?: ResourceConfig): void {
      if (!config) {
        config = {
          amount: 1,
          profit: 1,
          load: 1
        };
      }
      const resource: FormGroup = this.createResource(config);
      this.control.push(resource);
    }
    
    private createResource({ amount, profit, load }: ResourceConfig): FormGroup {
      return new FormGroup({
        amount: new FormControl(amount),
        profit: new FormControl(profit),
        load: new FormControl(load)
      });
    }
    

    这让createResource() 不必担心可能出现的undefined 变量。如果您想让amount、profit 或load 为零,这种方式可能会更好。注意0 || 1 等于1,因为0 是假的。如果您不想让0 神奇地变成1,则不能使用|| 1 技巧。


    无论哪种方式都可以,并且存在许多其他解决方案。这里的主要思想是尝试利用 TypeScript 的类型系统而不是规避它。

    好的,希望对您有所帮助。祝你好运!

    【讨论】:

    • 哇,期待如此广泛的答案! Partial 是我缺少的概念,我总是习惯于写入不同的接口。现在有很多东西要重构。非常感谢!
    猜你喜欢
    • 2019-05-09
    • 2022-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    • 2017-01-14
    • 1970-01-01
    相关资源
    最近更新 更多