TypeScript矢印関数で戻り値の型を指定する


118

私はReactとReduxを使用していて、アクションタイプをインターフェースとして指定しているため、レデューサーはタグ付きユニオンタイプを利用してタイプセーフティを改善できます。

したがって、次のような型宣言があります。

interface AddTodoAction {
    type: "ADD_TODO",
    text: string
};

interface DeleteTodoAction {
    type: "DELETE_TODO",
    id: number
}

type TodoAction = AddTodoAction | DeleteTodoAction

これらのアクションを作成するヘルパー関数を作成したいと思います。これには、矢印関数を使用する傾向があります。これを書けば:

export const addTodo1 = (text: string) => ({
    type: "ADD_TODO",
    text
});

AddTodoAction戻り値の型が明示的に指定されていないため、コンパイラーはこれが有効であることを確認するのに役立つことはありません。これを行うことで、戻り値の型を明示的に指定できます。

export const addTodo2: (text: string) => AddTodoAction = (text: string) => ({
    type: "ADD_TODO",
    text
})

ただし、これには関数の引数を2回指定する必要があるため、冗長で読みにくくなります。

矢印表記を使用するときに戻り値の型を明示的に指定できる方法はありますか?

私はこれを試すことを考えました:

export const addTodo3 = (text: string) => <AddTodoAction>({
    type: "ADD_TODO",
    text
})

この場合、コンパイラーは戻り値の型を推測するようになりましたが、返すAddTodoActionオブジェクトに適切なフィールドがすべてあるかどうかは検証されません。

これを解決するには、別の関数構文に切り替えます。

export const addTodo4 = function(text: string): AddTodoAction {
    return {
        type: "ADD_TODO",
        text
    }
}

export function addTodo5(text: string): AddTodoAction {
    return {
        type: "ADD_TODO",
        text
    }
}

これらの方法のいずれかにより、コンパイラーは正しい戻り値の型を使用し、すべてのフィールドを適切に設定したことを強制しますが、これらはより冗長でありthis、関数での' 'の処理方法を変更します(これは問題ではないかもしれませんが、私は考えます。)

これを行う最善の方法についてアドバイスはありますか?


6
getTitle = ():string => 'State Lists'
— Vikas Bansal 2017年

回答:


147

最初に、元の質問から次の表記を検討してください。

export const addTodo3 = (text: string) => <AddTodoAction>({
    type: "ADD_TODO",
    text
})

この表記法を使用して、返されたオブジェクトをtypeに型キャストしますAddTodoAction。ただし、関数の宣言された戻り値の型はまだ定義されていません(コンパイラーはany戻り値の型として暗黙的に想定します)。

代わりに次の表記を使用してください。

export const addTodo3 = (text: string): AddTodoAction => ({
    type: "ADD_TODO",
    text: text
})

この場合、必要なプロパティを省略すると、予期されるコンパイラエラーが発生します。たとえば、textプロパティを省略すると、次の(望ましい)エラーが生成されます。

Type '{ type: "ADD_TODO"; }' is not assignable to type 'TodoAction'.
  Type '{ type: "ADD_TODO"; }' is not assignable to type 'DeleteTodoAction'.
    Types of property 'type' are incompatible.
      Type '"ADD_TODO"' is not assignable to type '"DELETE_TODO"'.

遊び場の例もご覧ください。


これはまさに私が探していた表記法です。残念ながら、Visual Studio Codeは構文を私の期待に沿う方法で強調表示していませんが、コンパイラーはそれを理解しているようです。
— Brian Hanechak 16年

1

私はあなたの最善の策は正しいタイプを持つ関数のインターフェイスを作成することだと思います、そしてあなたはあなたのインターフェイスのすべてのネストされたタイプではなく、そのタイプを指定する必要があるだけです:

interface AddTodoAction {
    type: "ADD_TODO",
    text: string
};

interface AddTodoActionCreator {
    (text: string): AddTodoAction;
};

export const addTodo: AddTodoActionCreator = (text) => ({
    type: "ADD_TODO",
    text
});

更新:タイプでこれを行う方法

export interface GeneralAction<T> {
    type: string;
    payload: T;
}

export interface GeneralActionCreator<T> {
    (payload: T): GeneralAction<T>;
}

export const SAVE_EVENT = 'SAVE_EVENT';

export const SaveEvent: GeneralActionCreator<UserEvent> = (payload) => { return {type: SAVE_EVENT, payload}; };

2
この例ではジェネリックをどのように使用しますか?AddTodoActionCreator <T>としましょう
— Jannic Beck

1
UserEventここは何ですか?
— 1252748
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.