enum Unit {
Kilometer,
Meter,
}
interface Measurement {
distanceUnit: Unit
distance: number
}
interface MetersMeasurement {
distanceUnit: Unit.Meter
}
function someCalculation(item: MetersMeasurement){
return;
}
function main(){
const item : Measurement = {
distance: 100,
distanceUnit: Unit.Meter
}
if(item.distanceUnit !== Unit.Meter) return;
someCalculation(item)
// ^^^^ Types of property 'distanceUnit' are incompatible.
// Type 'Unit' is not assignable to type 'Unit.Meter'.
}
If
MetersMeasurement
andMeasurement
had the same fields, we could use a type predicate to narrow it down. But my problem requires thatMetersMeasurement
will only have a subset of the fields ofMeasurement
. Therefore a type predicate wouldn't work.We could also change the type of
distanceUnit
in the interfaceMetersMeasurement
toUnit
. And then move theif
check for meters insidesomeCalculation
function. But let's also say I don't want to do that. I don't want to give the function this responsibility.We could create a new object of type
MetersMeasurement
based on the original object and then pass that tosomeCalculation()
instead. But this feels like a workaround (and extra code) because we couldn't narrow down the type [playground].
Is there a way to properly narrow down the type without breaking the rules? (no any
or casting)
CodePudding user response:
You can use a type guard to check whether or not the item is MetersMeasurement
or Measurement
enum Unit {
Kilometer,
Meter,
}
interface Measurement {
distanceUnit: Unit
distance: number
}
interface MetersMeasurement {
distanceUnit: Unit.Meter
}
function someCalculation(item: MetersMeasurement){
return;
}
function isMeasurement(obj: Measurement): obj is Measurement {
return (obj as Measurement).distance !== undefined;
}
function main(){
const item = {
distance: 100,
distanceUnit: Unit.Meter
}
if(isMeasurement(item)) return;
someCalculation(item)
}
CodePudding user response:
Try this:
enum Unit {
Kilometer,
Meter,
}
type Distance = {
distance: number;
}
type MetersMeasurement = {
distanceUnit: Unit.Meter;
} & Distance
type KilometersMeasurement = {
distanceUnit: Unit.Kilometer;
} & Distance
type Measurement = MetersMeasurement | KilometersMeasurement;
/*
* return km
*/
function meterCalculation(item: MetersMeasurement) {
return item.distance * .001;
}
function main(){
const item : Measurement = {
distance: 100,
distanceUnit: Unit.Meter
}
if (item.distanceUnit == Unit.Meter) {
meterCalculation(item);
}
}