根据m
是通过三元表达式还是通过if-else语句指定的,类型检查器在print()
中键入的m
似乎有所不同。print()
函数中的第一行和该行下面注释的代码之间有什么区别?
interface Measurement {
unit: string;
}
interface ComputedMeasurement extends Measurement {
compute: (n: number) => number;
}
const measurements = {
'Vitamin D': {
unit: 'mcg',
},
};
const computedMeasurements = {
'Vitamin D (IU)': {
unit: 'IU',
compute: (n: number) => n / 0.025,
},
};
type MeasurementKey = keyof typeof measurements;
type ComputedMeasurementKey = keyof typeof computedMeasurements;
const isMeasurementKey = (k: string): k is MeasurementKey =>
k in measurements;
function print(key: MeasurementKey | ComputedMeasurementKey, amount: number): string {
const m = isMeasurementKey(key) ? measurements[key] : computedMeasurements[key];
// *** Replacing the above line with the below lines eliminates the below error. ***
// let m: Measurement | ComputedMeasurement;
// if (isMeasurementKey(key)) {
// m = measurements[key];
// } else {
// m = computedMeasurements[key];
// }
if ('compute' in m) {
// Error: Property 'compute' does not exist on type '{ unit: string; }'.
return `${m.compute(amount)} ${m.unit}`;
}
return `${amount} ${m.unit}`;
}
游乐场链接
有趣的是,let声明后面的if-then-else赋值保留了m
的并集类型Measurement | ComputedMeasurement
(这是预期行为(,但条件表达式将类型缩小到Measurement
,这会触发类型错误。
事实证明,这是条件表达式的预期行为,其中一个选项是另一个选项的子类型,正如本期已关闭的TypeScript问题中所解释的那样。其中一条评论提到,亚型减少有时可能有点不幸,您的例子就是其中之一。
如果使用as const
将measurements
和computedMeasurements
声明为只读对象,则可以使用条件表达式而不会出现任何错误,如计算的测量将不再是测量的子类型:
const measurements = {
'Vitamin D': {
unit: 'mcg',
},
} as const;
const computedMeasurements = {
'Vitamin D (IU)': {
unit: 'IU',
compute: (n: number) => n / 0.025,
},
} as const;
但是你也可以使用if-then-else赋值。
TypeScript游乐场