124

いくつかのプロパティも保持されている関数オブジェクトを作成したいと思います。たとえば、JavaScriptでは次のようにします。

var f = function() { }
f.someValue = 3;

TypeScriptでは、このタイプを次のように説明できます。

var f: { (): any; someValue: number; };

しかし、キャストを必要とせずに実際に構築することはできません。そのような:

var f: { (): any; someValue: number; } =
    <{ (): any; someValue: number; }>(
        function() { }
    );
f.someValue = 3;

キャストなしでこれをどのように構築しますか?

4

9 に答える 9

118

更新: この回答は、TypeScript の以前のバージョンでは最適なソリューションでしたが、新しいバージョンではより良いオプションを利用できます (他の回答を参照)。

受け入れられた答えは機能し、状況によっては必要になるかもしれませんが、オブジェクトを構築するための型安全性を提供しないという欠点があります。この手法は、未定義のプロパティを追加しようとすると、少なくとも型エラーをスローします。

interface F { (): any; someValue: number; }

var f = <F>function () { }
f.someValue = 3

// type error
f.notDeclard = 3
于 2013-09-05T15:12:25.670 に答える
60

TypeScript は、宣言のマージを通じてこのケースを処理するように設計されています。

また、関数を作成し、関数にプロパティを追加して関数をさらに拡張する JavaScript の慣例にも精通している場合があります。TypeScript は、宣言のマージを使用して、このような定義をタイプ セーフな方法で構築します。

宣言のマージにより、何かが関数と名前空間 (内部モジュール) の両方であると言えます。

function f() { }
namespace f {
    export var someValue = 3;
}

これにより、入力が保持され、 と の両方f()を記述できますf.someValue.d.ts既存の JavaScript コードのファイルを作成する場合は、次を使用しdeclareます。

declare function f(): void;
declare namespace f {
    export var someValue: number;
}

関数にプロパティを追加することは、TypeScript では混乱したり予期しないパターンであることが多いため、避けるようにしてください。ただし、古い JS コードを使用または変換するときに必要になる場合があります。これは、内部モジュール (名前空間) と外部モジュールを混在させることが適切な唯一のケースの 1 つです。

于 2015-10-28T13:47:05.793 に答える
33

そのため、その関数をキャストせずにビルドして "f" に割り当てるだけでよい場合は、次のような解決策が考えられます。

var f: { (): any; someValue: number; };

f = (() => {
    var _f : any = function () { };
    _f.someValue = 3;
    return _f;
})();

基本的に、割り当てが完了する前に、自己実行関数リテラルを使用して、その署名に一致するオブジェクトを「構築」します。唯一の奇妙な点は、関数の内部宣言が「a​​ny」型である必要があることです。そうしないと、オブジェクトにまだ存在しないプロパティに割り当てているとコンパイラが叫びます。

編集: コードを少し簡略化しました。

于 2012-10-07T16:33:12.423 に答える
2

ショートカットとして、['property'] アクセサーを使用してオブジェクト値を動的に割り当てることができます。

var f = function() { }
f['someValue'] = 3;

これにより、型チェックがバイパスされます。ただし、同じ方法で意図的にプロパティにアクセスする必要があるため、かなり安全です。

var val = f.someValue; // This won't work
var val = f['someValue']; // Yeah, I meant to do that

ただし、プロパティ値の型チェックが本当に必要な場合、これは機能しません。

于 2012-10-11T04:38:37.403 に答える
2

更新された回答: を介した交差タイプの追加以来、&その場で 2 つの推論されたタイプを「マージ」することが可能です。

これは、いくつかのオブジェクトのプロパティを読み取り、fromそれらを object にコピーする一般的なヘルパーですonto。同じオブジェクトを返しますontoが、両方のプロパティ セットを含む新しい型を使用するため、実行時の動作が正しく記述されています。

function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
    Object.keys(from).forEach(key => onto[key] = from[key]);
    return onto as T1 & T2;
}

この低レベル ヘルパーは型アサーションを実行しますが、設計上は型安全です。このヘルパーを配置すると、OPの問題を完全な型安全性で解決するために使用できる演算子ができます。

interface Foo {
    (message: string): void;
    bar(count: number): void;
}

const foo: Foo = merge(
    (message: string) => console.log(`message is ${message}`), {
        bar(count: number) {
            console.log(`bar was passed ${count}`)
        }
    }
);

TypeScript Playground で試してみるには、ここをクリックしてくださいfooを typeに制約しているFooため、 の結果はmergecomplete でなければならないことに注意してくださいFoobarそのため、名前を に変更するとbad、型エラーが発生します。

: ただし、ここにはまだ 1 つのタイプの穴があります。TypeScript は、型パラメーターを「関数ではない」ように制約する方法を提供しません。そのため、混乱して関数を の 2 番目の引数として渡すことができますが、mergeそれは機能しません。したがって、これが宣言されるまでは、実行時にキャッチする必要があります。

function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
    if (typeof from !== "object" || from instanceof Array) {
        throw new Error("merge: 'from' must be an ordinary object");
    }
    Object.keys(from).forEach(key => onto[key] = from[key]);
    return onto as T1 & T2;
}
于 2016-03-10T19:14:36.893 に答える
0

これは厳密な型付けとは異なりますが、次のことができます

var f: any = function() { }
f.someValue = 3;

この質問を見つけたときのように、抑圧的な強力なタイピングを回避しようとしている場合。悲しいことに、これは TypeScript が完全に有効な JavaScript で失敗するケースであるため、TypeScript に取り下げるように指示する必要があります。

「あなたの JavaScript は完全に有効な TypeScript です」は false と評価されます。(注: 0.95 を使用)

于 2014-02-21T15:59:26.817 に答える