2017-05-09 9 views
1

例としてthis StackOverflow postを使用して、型付きイベントシステムを実装しました。TypeScriptの汎用関数でマップ型を推測する

function handleEvent<T extends keyof MyTypeMap>(eventKey: T, eventMsg: MyTypeMap[T]) { 
    switch(eventKey) { 
    case('FOO'): 
     // TS believes that eventKey is of type 'never' 
     // TS believes that eventMsg is 'string|number' 
     break; 
    case('BAR'): 
     // TS believes that eventKey is of type 'never' 
     // TS believes that eventMsg is 'string|number' 
     break; 
    } 
} 

活字体は、この機能が外部から呼び出されたときに、期待通りに動作します。私はこのマップを利用し、イベントハンドラを作成しようとしてい

interface MyTypeMap { 
    FOO: string; 
    BAR: number; 
} 

:のように簡体字は、それが見えます。たとえば、次のようになります。

ただし、関数TypeScriptの内部は非常に混乱しています。それは、事例のどれもがヒットすることはないと信じているようです。 TypeScriptは、関数内の型を正しく推論することができないのですが、外部呼び出しで正常に動作しています。

答えて

1

現在、別の変数の型に基づいて1つの変数の型を制約する方法はありません。 (何のアイデアなぜそれがneverとしてeventKey型を推論するtypescriptですが生じない)あなたは、一般的なパラメータにhandleEventTを削除した後に得ることができる最高のは、変数の型ので、コンパイラはeventMsgの種類を制限するものではありません

function handleEvent(eventKey: keyof MyTypeMap, eventMsg: MyTypeMap[typeof eventKey]) { 
    switch(eventKey) { 
    case ('FOO'): 
      const a1 = eventKey; // a1 has type 'FOO' 
      const m1 = eventMsg; // m1 has type string|number 
     break; 
    case('BAR'): 
      const a2 = eventKey; // a2 has type 'BAR' 
      const m2 = eventMsg; // again, m2 has type string|number 
     break; 
    } 
} 

ですそれは、に依存するeventKey、制限されています。それができればそれはうまくいく。

しかし、switch文ではなくハンドラにメッセージ型をマップするオブジェクトを使用する場合は、プロトタイプが動作しています(現在はメッセージ型ごとにハンドラが1つしかありませんが、簡単に拡張できます) :

class Dispatcher {  
    on< 
     MessageType extends keyof AppMessageMap 
    >(
     messageType: MessageType, 
     handler: (message: AppMessageMap[MessageType]) => void 
    ): void { 
     this.handlerMap[messageType] = handler; 
    } 

    handlerMap: {[s: string]: (message: AppMessageMap[keyof AppMessageMap]) => void} = {}; 

    emit<MessageType extends keyof AppMessageMap>(messageType: MessageType, message: AppMessageMap[MessageType]) { 
     const handler = this.handlerMap[messageType]; 
     if (handler) { 
      handler(message); 
     } 
    } 
} 

/* messages.ts */ 

interface AddCommentMessage { 
    commentId: number; 
    comment: string; 
    userId: number; 
} 

interface PostPictureMessage { 
    pictureId: number; 
    userId: number; 
} 

interface AppMessageMap { 
    "ADD_COMMENT": AddCommentMessage, 
    "POST_PICTURE": PostPictureMessage 
} 

/* app.ts */ 
const dispatcher = new Dispatcher(); 


dispatcher.on("ADD_COMMENT", (message) => { 
    console.log(`add comment: ${message.comment}`); 
}); 
dispatcher.on("POST_PICTURE", (message) => { 
    console.log(`post picture: ${message.pictureId}`); 
}); 

dispatcher.emit('ADD_COMMENT', { 
    comment: 'some comment', 
    commentId: 2, 
    userId: 3 
}); 
dispatcher.emit('POST_PICTURE', { 
    pictureId: 4, 
    userId: 5 
}); 
関連する問題