如何检查 TypeScript 中产品类型的详尽性?
How to check for exhaustiveness of product types in TypeScript?
在 TypeScript 中检查求和类型的详尽性非常容易。
type Sum =
| { tag: 'num'; value: number }
| { tag: 'str'; value: string };
const len = (sum: Sum): number => {
switch (sum.tag) {
case 'num': return sum.value;
case 'str': return sum.value.length;
default: {
const unhandled: never = sum;
throw new Error(`Unhandled sum ${unhandled}`);
}
}
};
现在,如果我向 Sum
类型添加新变体,则 sum
将不再可分配给 unhandled
。因此,我们将因非详尽性而出现编译时错误。
如何在 TypeScript 中对产品类型执行相同的操作?考虑以下示例。
type Product = {
num: number;
str: string;
};
const repeat = (product: Product): string => {
const { num, str } = product;
return str.repeat(num);
};
现在,如果我向 Product
类型添加一个新的 属性,那么我希望 TypeScript 编译器报告一个非详尽性错误,因为新的 属性 没有'没有被解构和使用。我该怎么做?
如果代码因非详尽性而抛出运行时错误,则加分。
让我们从抛出一个运行时错误开始,因为它不详尽。我们可以通过解构其余属性来实现这一点,如果它有一个或多个可枚举键则抛出错误。
const repeat = (product: Product): string => {
const { num, str, ...props } = product;
if (Object.keys(props).length > 0) {
throw new Error(`Unhandled props ${props}`);
}
return str.repeat(num);
};
接下来,为了让 TypeScript 在编译时检查穷尽性,我们可以执行以下操作。
const repeat = (product: Product): string => {
const { num, str, ...props } = product;
const unhandled: {} extends typeof props ? {} : never = props;
if (Object.keys(unhandled).length > 0) {
throw new Error(`Unhandled props ${unhandled}`);
}
return str.repeat(num);
};
这是它的工作原理。
- 空对象类型
{}
只能分配给 typeof props
当且仅当 props
是一个空对象。
- 因此,当
props
为空对象时,unhandled
的类型为{}
,一切正常。
- 然而,当
props
不是空对象时,unhandled
的类型是 never
,我们会得到一个编译时错误。
因此,上述代码将在解构时检查产品类型的详尽性。如果将新的 属性 添加到 Product
类型,则 props
将不再可分配给 unhandled
并且我们将因非详尽性而出现编译时错误。
此外,您可以启用@typescript-eslint/no-unused-vars
规则以确保使用所有解构的属性。确保将 ignoreRestSiblings
选项设置为 false
。
在 TypeScript 中检查求和类型的详尽性非常容易。
type Sum =
| { tag: 'num'; value: number }
| { tag: 'str'; value: string };
const len = (sum: Sum): number => {
switch (sum.tag) {
case 'num': return sum.value;
case 'str': return sum.value.length;
default: {
const unhandled: never = sum;
throw new Error(`Unhandled sum ${unhandled}`);
}
}
};
现在,如果我向 Sum
类型添加新变体,则 sum
将不再可分配给 unhandled
。因此,我们将因非详尽性而出现编译时错误。
如何在 TypeScript 中对产品类型执行相同的操作?考虑以下示例。
type Product = {
num: number;
str: string;
};
const repeat = (product: Product): string => {
const { num, str } = product;
return str.repeat(num);
};
现在,如果我向 Product
类型添加一个新的 属性,那么我希望 TypeScript 编译器报告一个非详尽性错误,因为新的 属性 没有'没有被解构和使用。我该怎么做?
如果代码因非详尽性而抛出运行时错误,则加分。
让我们从抛出一个运行时错误开始,因为它不详尽。我们可以通过解构其余属性来实现这一点,如果它有一个或多个可枚举键则抛出错误。
const repeat = (product: Product): string => {
const { num, str, ...props } = product;
if (Object.keys(props).length > 0) {
throw new Error(`Unhandled props ${props}`);
}
return str.repeat(num);
};
接下来,为了让 TypeScript 在编译时检查穷尽性,我们可以执行以下操作。
const repeat = (product: Product): string => {
const { num, str, ...props } = product;
const unhandled: {} extends typeof props ? {} : never = props;
if (Object.keys(unhandled).length > 0) {
throw new Error(`Unhandled props ${unhandled}`);
}
return str.repeat(num);
};
这是它的工作原理。
- 空对象类型
{}
只能分配给typeof props
当且仅当props
是一个空对象。 - 因此,当
props
为空对象时,unhandled
的类型为{}
,一切正常。 - 然而,当
props
不是空对象时,unhandled
的类型是never
,我们会得到一个编译时错误。
因此,上述代码将在解构时检查产品类型的详尽性。如果将新的 属性 添加到 Product
类型,则 props
将不再可分配给 unhandled
并且我们将因非详尽性而出现编译时错误。
此外,您可以启用@typescript-eslint/no-unused-vars
规则以确保使用所有解构的属性。确保将 ignoreRestSiblings
选项设置为 false
。