2017-01-28 10 views
7

RoslynがC#を提供するものと同様に、tscが蒸散プロセスを開始する前に.tsファイルを変更/チェックしたいと思います。Typescriptのカスタムツール、transpiler、拡張子

これは静的型検査に便利です。例えば、適切かつ短いImmutable Record実装は、それがFlowで行うことができるように、コンパイル時にコード変更/静的なチェックが必要です。

@Record(Person) 
interface IPerson { 
    givenName: string; 
    familyName: string; 
} 

、その後、カスタムTSC transpilerはにコードを修正することができます:

interface IPersonParams { 
    givenName?: string; 
    familyName?: string; 
} 
@Record() 
class Person { 
    private readonly __givenName; 
    private readonly __familyName; 
    constructor(init) { 
    this.__givenName = init.givenName; 
    this.__familyName = init.familyName; 
    } 
    get givenName() { 
    return this.__givenName; 
    } 
    get familyName() { 
    return this.__familyName; 
    } 
    update(update: IPersonParams) { 
    // ignore the bug when undefined param is passed 
    return new Person({ 
     givenName: update.givenName || this.__givenName, 
     familyName: update.familyName || this.__familyName 
    }); 
    } 
} 

特別なtsc watchを実行するVisual StudioとVisual Studioコードでは、webpackバンドルやカスタムgulpタスクの一部としてではなく、カスタムコンパイルエラーがすぐに表示されます。 TypescriptのAPIがありますが、それはtscVS/VS Code/Atomでシームレスに動作するようにするにはどうすればいいですか?例


更新の目標は、単に

@Record(Person) 
interface IPerson { 
    givenName: string; 
    familyName: string; 
} 
  1. クラスPersonが自動になり先に示したようにインターフェイスIPersonに基づいて生成された書き込みすることです。

  2. オブジェクトをインスタンス化することが可能となります。

    let instance = new Person({givenName: "Emma", familyName: "Watson"});

    どれでも間違ったプロパティは、コンパイル・エラーが発生します:

    let instance = new Person({nonExistedProperty: "Emma"}); //error

    エラー:プロパティ 'nonExistedProperty' が存在しません。 in class Personコンストラクタ。
    エラー:クラスPersonコンストラクタで 'givenName'プロパティが必要です。
    エラー:プロパティ「FamilyNameでは、」Personクラスのコンストラクタに必要とされます。

  3. 既存のオブジェクトは、部分的にすべてのプロパティが

    let instance = new Person({givenName: "Emma", familyName: "Watson"});読み取り専用です

    let instance = new Person({givenName: "Emma", familyName: "Watson"}); instance.Update({givenName: "Luise"});

    instance.givenName === "Luise"; //TRUE;
    instance.familyName === "Watson"; //TRUE;

  4. 更新されることができるはずですinstance.givenName = "John"; //error

    エラー:プロパティ 'givenName属性が' 読み取り専用です。

  5. Equals方法が自動生成されます。ハッシュや他の何かをベースにすることもできますが、すばやく作業して詳細なチェックを行う必要があります。

    let instance1 = new Person({givenName: "Emma", familyName: "Watson"});
    let instance2 = new Person({givenName: "Emma", familyName: "Watson"});

    instance1.Equals(instance2); //TRUE

    また、作成されたインスタンスを制御するための場所を持っているかもしれないし、同じパラメータを持つレコードが内部ディクショナリに存在する場合、それはちょうどこのオブジェクトへの参照を返します。

    let instance1 = new Person({givenName: "Emma", familyName: "Watson"});
    let instance2 = new Person({givenName: "Emma", familyName: "Watson"});

    instance1 == instance2; //TRUE
    instance1 === instance2; //TRUE

+0

使用すると、1つまたは複数の使用例をお願いできますか? あなたのデコレータがあなたの想像通りに解釈されたとき、どのようにユーザーコードが見えるでしょうか?何が可能で、何が制限されるのでしょうか? – Benjamin

+0

TypeScriptでインターフェイスに注釈を付けることができないという事実を覚えておいてください。ここでの回避策は、インターフェースの代わりにクラスを使うことです: 'interface A {} ... class B implements A {}' – Benjamin

+0

@Benjamin問題はインターフェースの注釈より深いです。コンパイラは動的に作成されたオブジェクトを完全にチェックすることはできません。 Typescriptアノテーションは、動的に作成されたオブジェクトのみを許可しますが、静的ではありません。そのため、カスタム中間コンパイルツールが必要です。これは、コンパイラによってさらに完全にチェックされるクラス(静的)を作成することができます。 – Artru

答えて

1

たぶん代わりに、独自のtypescriptです(事前に)プロセッサを書いて、あなたはtypescriptですデコレータを使って、あなたの目標を達成することができました。

この例はhttps://www.typescriptlang.org/docs/handbook/decorators.htmlからです:

@sealed 
class Greeter { 
    greeting: string; 
    constructor(message: string) { 
     this.greeting = message; 
    } 
    greet() { 
     return "Hello, " + this.greeting; 
    } 
} 

function sealed(constructor: Function) { 
    Object.seal(constructor); 
    Object.seal(constructor.prototype); 
} 
+0

ありがとうございます。これをチェックするのは私の最初の試みでしたが、問題があります。オブジェクトの構造を正しく理解するためには、コードは静的解析の準備が整っているはずです。 これは、 'obj [someVarProp] = propValue'のような動的オブジェクトを作成すると、コンパイラがコードの正確性を理解してチェックできないことを意味します。したがって、この手順は、Roslynプロジェクトで行うことができるように、コンパイラと緊密に行う必要があります。 – Artru

1

これは私がこれまでに見つかったものです。この瞬間(2017年)にそれを行う簡単な方法はありません。

解決策の1つは、Typescript APIを使用するカスタムプラグインを作成することです。また、TSサービスと並行して、独自のサンドボックスで2回目のサービスをVS Code,AtomまたはVSに実行します。これに加えて、各IDEでは、独自のプラグインをコアプラグイン/サービスの上にラッパーとして作成する必要があります。

このように、一部の人々は既にvscode-ng-language-serviceng2linterのようにリンターを作っています。

マイクロソフトでは、#6508 TypeScript拡張性のチケットを用意しています。これにより、要求された機能を実装することが可能になり、簡単になります。 C#でプログラミングする人とF#については


ではなく活字体の拡張を待っているのRoslynの可能性を使用する方がよいかもしれません。 C#で書かれたコードはTypeScriptまたはJavaScriptに変換できます。また、あらゆる種類のチェックやカスタム修正の可能性が広がります。もちろん、同じロジックが.NETTypeScript/Javascriptにある場合は、DRY principleに近いです。 Bridge.NETはRoslynを使用していませんが、実装は良好です。 Rosetta .NET 4とRoslynのプロジェクトは良いスタートだと思われます。

1

これはコンパイラの魔法なしでも可能です。

外部LIBS

declare function someGenericEqualsFn(a, b): boolean; 
declare function makeCached<TResult, TFunc extends (...args) => TResult>(funcToCache: TFunc): TFunc; 
declare function merge<T>(objectToUpdate: T, objectToMerge: Partial<T>); 

当社のlib:

interface DataRecord<TRecord> { 
    equals<TRecord>(other: TRecord): boolean; 
    update(update: Partial<TRecord>); 
} 

function createDataRecord<TRecord>(data: TRecord): Readonly<TRecord> & DataRecord<TRecord> { 
    const result: TRecord & DataRecord<TRecord> = <any>{}; 
    Object.keys(data).forEach(() => { 

    }); 

    result.equals = function (other: TRecord) { 
     return someGenericEqualsFn(result, other); 
    }; 

    result.update = function (partial: Partial<TRecord>) { 
     merge(result, partial); 
    }; 

    return result; 
} 

我々のテスト:

interface IPerson { 
    givenName: string; 
    familyName: string; 
} 

let instance = createDataRecord<IPerson>({givenName: "Emma", familyName: "Watson"}); 
instance = createDataRecord<IPerson>({nonExistedProperty: "Emma"}); // compiler error 
instance.givenName = "John";    // compiler error 
instance.update({givenName: "Emma"});  // works! 
instance.update({nonExistedProperty: "x"});  // compiler error 

const createDataRecordOrGetCached = makeCached(createDataRecord); 
let instance1 = createDataRecordOrGetCached({givenName: "Emma", familyName: "Watson"}); 
let instance2 = createDataRecordOrGetCached({givenName: "Emma", familyName: "Watson"}); 

instance1 == instance2; //TRUE 
instance1 === instance2; //TRUE 
+0

これは一般的な質問には答えませんが、私の大きなプロジェクトの問題の1つを解決します。あなたに助けてくれてありがとう@ベンジャミン!非常に、非常に便利です!マップされた型は実際に部分的な更新と読み取り専用フィールドで問題を解決します。 – Artru