2017-02-01 4 views
2

Sagen wir mal diskriminieren ich diskriminiert Union-Typ haben Redux Aktionen darzustellen:Wie eine diskriminiert Union Typ

interface AddTodoAction { type: 'ADD_TODO'; description: string; } 
interface RemoveTodoAction { type: 'REMOVE_TODO'; id: number; } 
type Action = AddTodoAction | RemoveTodoAction; 

Wenn ich eine Karte von Aktionstypen zu Reduzierungen machen wollte, die sie handhaben, ich könnte mit Start:

type ActionReducers = { 
    [P in Action['type']]: (state: State, action: Action) => State 
}; 

jedoch das zweite Argument (action: Action) zu allgemein ist. Ich würde gerne sagen "die Action mit type entsprechend P", aber ich weiß nicht, ob es existiert. Ich versuchte Action & {type: P}, aber diese Art tut das Gegenteil.

Irgendwelche Ideen?

+0

ich glaube, die nächste Sie auf das gewünschte Verhalten zu bekommen, ist typeguards ein Reduktions wie dies ohne Fehler kompiliert werden: '(Stand: Staat, Aktion: Aktion) => { Schalter (action.type) { Fall 'ADD_TODO': action.description; Rückkehrzustand; Fall 'REMOVE_TODO': action.id; Rückkehrzustand; } } ' hmpf Ich vermisse echte Code-Formatierung in Kommentaren – Kalle

Antwort

1

TypeScript lässt Sie den Typ einer markierten Union nicht automatisch nachschlagen. Es ist eine nette Idee, also möchten Sie vielleicht make a suggestion. Die Logik ist bereits im Rahmen der Kontrollflussanalyse implementiert; vielleicht könnte es als ein Typ-Operator irgendeiner Art entlarvt werden.


In Ermangelung dieser Funktion gibt es Workarounds. Der einfachste Weg ist, genau das Gegenteil zu erklären, selbst abbildet und dann darauf zu verweisen, wenn Sie es brauchen, auf Kosten einiger Wiederholung:

type ActionMapping = { 
    ADD_TODO: AddTodoAction; 
    REMOVE_TODO: RemoveTodoAction; 
} 
interface Action { type: keyof ActionMapping }; // useful for consistency 
interface AddTodoAction extends Action { 
    type: 'ADD_TODO'; // manually cross-reference 
    description: string; 
} 
interface RemoveTodoAction extends Action { 
    type: 'REMOVE_TODO'; // manually cross-reference 
    id: number; 
} 
// if you want more Action types you need to add it to ActionMapping: 
interface BadAction extends Action { 
    type: 'BAD'; // error, BadAction incorrectly extends Action 
    title: string; 
} 

Jetzt können Sie definieren, was Sie wollen als:

type ActionReducers = { 
    [P in keyof ActionMapping]: (state: State, action: ActionMapping[P]) => State 
}; 

Hier ist eine andere Art und Weise mit weniger Vervielfältigung, aber das ist mehr verworren:

// define all your data types here without the type property 
type ActionDataMapping = { 
    ADD_TODO: { description: string }, 
    REMOVE_TODO: { id: number } 
} 

// the ActionMapping programmatically adds the right tag to the data 
type ActionMapping = { 
    [K in keyof ActionDataMapping]: ActionDataMapping[K] & { type: K }; 
} 

// and an Action is just the union of values of ActionMapping properties  
type Action = ActionMapping[keyof ActionMapping]; 

// this is the same as above 
type ActionReducers = { 
    [P in keyof ActionMapping]: (state: State, action: ActionMapping[P]) => State 
}; 

Hier sollte auch alles funktionieren. Ihnen fehlen schöne Namen für Ihre Action Subtypen. Fügen Sie sie zurück, wenn Sie wollen, aber es ist ein wenig mehr Duplizierung:

// if you need names for them: 
type AddTodoAction = ActionMapping['ADD_TODO']; 
type RemoveTodoAction = ActionMapping['REMOVE_TODO']; 

Hoffnung eines jener Werke für Sie. Viel Glück!