2017-09-10 12 views
1

Adob​​eのExtendScriptは、画面上の距離を表すUnitValueタイプを備えています。これは、Numberタイプと類似していますが、それとは別のタイプです。TypeScriptのAdobeの数字のような「UnitValue」を表す

  1. UnitValueオブジェクトは、例えば"cm"又は"ft"ため、typeストリングを運びます。
  2. 異なるtypeの値を持つオブジェクトの間の算術演算には暗黙の強制が含まれます。 UnitValueオブジェクトとプレーンNumberの間の算術演算はそのまま発生し、UnitValueを返します。
  3. UnitValueオブジェクトには多くの雑多なフィールドがあります。 UnitValueプロトタイプには一連のメソッドが実装されています。
  4. UnitValueオブジェクトはコンストラクタ - var x = UnitValue(4, "cm")から構築されます。

これをTypeScriptでどのように表現すればよいでしょうか?

+0

新しいUnitValueはどのように作成しますか?コンストラクタはありますか?またはあなたが呼び出す必要がある機能? –

+0

'+'演算子を使って一緒に追加できますか?残念ながら、私が知る限り、Typescriptでそれをモデル化することはできません。 –

答えて

0

これを実装する1つの方法 - 私はベースユニットとしてセンチメーターを選択しましたが、他のものを使うことができました。上で述べたように、これは算術演算にインスタンスメソッドを使用しますが、TypeScriptの演算子をオーバーロードすることはできません。私は、引数ではなくメソッドが呼び出されたインスタンスの型(ユニット)を維持することを選択しました。

説明のためにコード内のコメントを参照してください。質問がある場合は質問してください。

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 
0

活字体におけるジェネリックとリテラル種類使用してユニットを実装することが技術的に可能:

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'> 
:次に、このようにそれを使用

// 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); 
    } 
} 

をこのように使用

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}`; 
    } 
} 

ただし、これはあまりにも多くの複雑さを導入すると主張する。

関連する問題