Adobe的ExtendScript采用UnitValue
类型,代表屏幕上的距离。它是一种与Number
类型相似但不同的类型。
UnitValue
对象都带有type
字符串,例如"cm"
或"ft"
。UnitValue
值的type
个对象之间的算术运算涉及隐式强制; UnitValue
对象与普通Number
之间的算术运算按原样发生,返回UnitValue
。UnitValue
对象都带有许多杂项字段; UnitValue
原型实现了一堆方法。UnitValue
对象是从构造函数构建的 - var x = UnitValue(4, "cm")
。我怎样才能在TypeScript中最好地表现这一点?
答案 0 :(得分:0)
实现它的一种方法 - 我选择厘米作为基本单位,但你可以使用别的东西。它使用实例方法进行算术运算,如上所述,您不能在TypeScript中重载运算符。我选择维护调用方法的实例的类型(单位)而不是参数。
请参阅代码中的注释以获取解释,但请询问您是否有任何疑问。
export interface IUnitValue {
add(a: IUnitValue): IUnitValue;
subtract(a: IUnitValue): IUnitValue;
toString(): string;
}
export type Unit = "cm" | "ft";
// UnitValue is a factory function that knows how to construct different
// types of UnitValueClass
export const UnitValue = (value: number, unit: Unit): IUnitValue => {
switch (unit) {
case "cm":
return new UnitValueClass(value, 1, unit);
case "ft":
return new UnitValueClass(value, 30, unit);
}
throw new Error(`Unrecognised unit ${unit}`);
};
export class UnitValueClass implements IUnitValue {
private value: number;
private cmPerUnit: number;
private type: Unit;
constructor(value: number, cmPerUnit: number, unit: Unit) {
this.value = value;
this.cmPerUnit = cmPerUnit;
this.type = unit;
}
// Return the wrapped value converted to centimeters
private toCm(): number {
return this.value * this.cmPerUnit;
}
// When adding, convert both operands to centimeters, then convert the result
// to the correct type and return a new UnitValue
add(a: this): IUnitValue {
return UnitValue((this.toCm() + a.toCm()) / this.cmPerUnit, this.type);
}
// Same logic as adding
subtract(a: this): IUnitValue {
return UnitValue((this.toCm() - a.toCm()) / this.cmPerUnit, this.type);
}
// Make it look pretty
toString() {
return `${this.value} ${this.type}`;
}
}
像这样使用:
const a = UnitValue(45, "cm");
const b = UnitValue(1, "ft");
console.log(a.toString()); // 45 cm
console.log(b.toString()); // 1 ft
console.log(b.add(a).toString()); // 2.5 ft
console.log(a.subtract(b).toString());// 15 cm
答案 1 :(得分:0)
技术上可以在TypeScript中使用泛型和文字类型实现单位:
// union of all possible unit types
type UnitType = 'cm' | 'm';
interface UnitConversion<From extends UnitType, To extends UnitType> {
from: From;
to: To;
convert(value: UnitValue<From>): UnitValue<To>;
}
function conversion<From extends UnitType, To extends UnitType>(
from: From, to: To, convert: (value: UnitValue<From>) => UnitValue<To>
): UnitConversion<From, To> {
return { from, to, convert };
}
function identity<T extends UnitType>(t: T): UnitConversion<T, T> {
return { from: t, to: t, convert: v => v };
}
// conversion table for each pair of unit types
const IMPLICIT_CONVERSIONS = {
'cm': {
'cm': identity('cm'),
'm': conversion('cm', 'm', v => new UnitValue(v.value * 0.1, 'm')),
},
'm': {
'cm': conversion('m', 'm', v => new UnitValue(v.value * 10, 'cm')),
'm': identity('m'),
},
};
type ImplicitConversions<
Left extends UnitType,
Right extends UnitType
> = (typeof IMPLICIT_CONVERSIONS)[Left][Right]['to'];
function convert(conversion: UnitConversion<any, any>, value: UnitValue<any>) {
return value.type === conversion.to ? value : conversion.convert(value);
}
type UnitPair<T extends UnitType> = {
left: UnitValue<T>;
right: UnitValue<T>;
};
function convertToCommonType<Left extends UnitType, Right extends UnitType>(
left: UnitValue<Left>,
right: UnitValue<Right>
): UnitPair<ImplicitConversions<Left, Right>> {
const conversion = IMPLICIT_CONVERSIONS[left.type][right.type];
return { left: convert(conversion, left), right: convert(conversion, right) };
}
class UnitValue<Type extends UnitType> {
constructor(
readonly value: number,
readonly type: Type,
) { }
/** Type-safe unit addition */
add<T extends UnitType>(value: UnitValue<T>): UnitValue<ImplicitConversions<Type, T>> {
const { left, right } = convertToCommonType(this, value);
return new UnitValue(left.value + right.value, left.type);
}
}
然后像这样使用它:
const common = convertToCommonType(
new UnitValue(3, 'cm'),
new UnitValue(10, 'm')
);
// => result type: UnitValue<'m'>
const z = new UnitValue(4, 'cm').add(new UnitValue(5, 'm'));
// => result type: UnitValue<'m'>
然而,可以说这引入了太多的复杂性。