4

我正在尝试编写一个函数,该函数将根据传递的键和参数执行特定的计算。我还想强制传递的键和参数之间的关系,所以我使用了一个带有约束的通用函数:

interface ProductMap {
  one: {
    basePrice: number;
    discount: number;
  },
  two: {
    basePrice: number;
    addOnPrice: number;
  }
}

function getPrice<K extends keyof ProductMap>(key: K, params: ProductMap[K]) {
  switch (key) {
    case 'one': {
      return params.basePrice - params.discount; // Property 'discount' does not exist on type 'ProductMap[K]'.
    }
    case 'two': {
      return params.basePrice + params.addOnPrice;
    }
  }
}

也许我以错误的方式考虑这个问题,但似乎打字稿应该能够缩小 switch 语句中的泛型类型。我可以让它工作的唯一方法就是这种尴尬:

function getPrice<K extends keyof ProductMap>(key: K, params: ProductMap[K]) {
  switch (key) {
    case 'one': {
      const p = params as ProductMap['one'];
      return p.basePrice - p.discount;
    }
    case 'two': {
      const p = params as ProductMap['two'];
      return p.basePrice + p.addOnPrice;
    }
  }
}

谁能解释为什么#1 不起作用或提供替代解决方案?

4

2 回答 2

4

“谁能解释为什么 #1 不起作用或提供替代解决方案?”

这就是#1 不起作用的原因:TypescriptkeyK.

检查将case 'one':变量的类型缩小key: Kkey: 'one'

但它并没有缩小 from K extends 'one' | 'two'to K extends 'one',因为没有对实际类型变量K进行测试,也无法进行任何测试来缩小它。So params: ProductMap[K]is still params: ProductMap[K],并且K仍然是同一个类型,所以 的类型params没有被缩小。


这是另一种解决方案:使用可区分的联合,并打开判别式(即__tag下面代码中的属性)。

type ProductMap =
  {
    __tag: 'one';
    basePrice: number;
    discount: number;
  } | {
    __tag: 'two';
    basePrice: number;
    addOnPrice: number;
  }

function getPrice(params: ProductMap): number {
  switch (params.__tag) {
    case 'one': {
      return params.basePrice - params.discount;
    }
    case 'two': {
      return params.basePrice + params.addOnPrice;
    }
  }
}

游乐场链接

于 2019-11-27T17:39:48.063 回答
1

确实,看起来 TypeScript 不是那么聪明,但是有一种解决方法,它比强制转换更好:

function getPrice(productMap: ProductMap, key: keyof ProductMap) {
  switch (key) {
    case 'one': {
      const params = productMap['one'];
      return params.basePrice - params.discount;
    }
    case 'two': {
      const params = productMap['two'];
      return params.basePrice + params.addOnPrice;
    }
  }
}
于 2019-11-27T17:24:34.430 回答