typescript 如何将第二个参数类型限制在第一个参数类型上?

ego6inou  于 2023-02-17  发布在  TypeScript
关注(0)|答案(1)|浏览(149)

在教程Extract parameter types from string literal types with TypeScript中有一个有趣的问题没有答案。

function calculate(operation, data) {
    if (operation === 'add') {
        return data.addend_1 + data.addend_2;
    } else if (operation === 'divide') {
        return data.dividend / data.divisor;
    }
}
 
calculate('add', { addend_1: 1, addend_2: 2 });
calculate('divide', { dividend: 42, divisor: 7 });

如何定义这样一个函数的类型?

umuewwlo

umuewwlo1#

一种方法是将operation参数表示为一个区分联合体的 discriminant 属性,它可以帮助您缩小data的表观类型。实际上,如果您将operationdata打包在一个对象中,而不是作为单独的参数,那么对于区分联合体来说,这将是一个简单的任务:

interface AddData { addend_1: number; addend_2: number };
interface DivData { dividend: number; divisor: number };
type DiscU =
  { operation: "add", data: AddData } |
  { operation: "divide", data: DivData };

function calculateD(arg: DiscU) {
  if (arg.operation === 'add') {
    return arg.data.addend_1 + arg.data.addend_2;
  } else if (arg.operation === 'divide') {
    return arg.data.dividend / arg.data.divisor;
  } else throw new Error("invalid operation");
}

calculateD({ operation: 'add', data: { addend_1: 1, addend_2: 2 } });
calculateD({ operation: 'divide', data: { dividend: 42, divisor: 7 } });

TypeScript 4.6引入了对反结构区分并集的支持,因此您可以将rest parameter的元组类型表示为一个区分并集,并将其反结构化为单独的参数,如下所示:

function calculate(
  ...[operation, data]: ["add", AddData] | ["divide", DivData]
) {
  if (operation === 'add') {
    return data.addend_1 + data.addend_2;
  } else if (operation === 'divide') {
    return data.dividend / data.divisor;
  } else throw new Error("invalid operation")
}

calculate('add', { addend_1: 1, addend_2: 2 });
calculate('divide', { dividend: 42, divisor: 7 });

Playground代码链接

相关问题