typescript - Generisk typ för typning av vuex-store


Jag försöker skapa en generisk typ som kommer att ge typningsledtrådar för Vuex-mutationer. Jag läste den här artikeln Vuex + TypeScript och blev inspirerad att skapa något mer generiskt. Jag kom på något som liknar detta:

export type MutationType<S, P, K extends keyof P> = Record<K, (state: S, payload: P[K]) => void>;

// S
export type DiceGameState = {
  round: number;
  score: number;
  diceItems: DiceHistoryItem[];
};

// P
export interface DiceGameMutationPayloadMap {
  INCREMENT_SCORE: number;
  DECREMENT_ROUND: number;
  ADD_DICE_HISTORY_ITEM: DiceHistoryItem;
}

// K
export enum DiceGameMutationsKeys {
  INCREMENT_SCORE = 'INCREMENT_SCORE',
  DECREMENT_ROUND = 'DECREMENT_ROUND',
  ADD_DICE_HISTORY_ITEM = 'ADD_DICE_HISTORY_ITEM'
}

export type DiceStoreMutation = MutationType<
  DiceGameState,
  DiceGameMutationPayloadMap,
  keyof typeof DiceGameMutationsKeys
>;

export const mutations: MutationTree<DiceGameState> & DiceStoreMutation = {
  DECREMENT_ROUND: (state: DiceGameState, payload: number) => {},
  INCREMENT_SCORE: (state: DiceGameState, payload: number) => {},
  ADD_DICE_HISTORY_ITEM: (state: DiceGameState, payload: DiceHistoryItem) => {}
};

Var :

  • S → typ av mutationsstatus
  • P → karta över mutationsnamn och typ av nyttolast som tilldelats denna mutation
  • K → mutationsnamn

Om jag kontrollerar detaljerna för denna MutationType i min IDE får jag denna ledtråd:

Så nyckeln i detta objekt är en av K-nycklarna och värdet är en metod som tar ett tillstånd som är av typen S och en nyttolast som är av typen tilldelat värde till P.

Om jag försöker kompilera den här koden får jag detta fel:

Fel:(7, 3) TS2322: Typen '(state: DiceGameState, payload: number) => void' kan inte tilldelas typen '(state: DiceGameState, payload: number | DiceHistoryItem) => void'. Typerna för parametrarna 'payload' och 'payload' är inkompatibla. Typen 'number | DiceHistoryItem' kan inte tilldelas typen 'number'. Typen 'DiceHistoryItem' kan inte tilldelas typen 'number'.

Vet du hur jag kan få min generiska att fungera?

Exempel :

Jag vill låta TS härleda typen av payload för ett givet mutationsnamn. Till exempel, om du har en mutation som heter INCREMENT_SCORE så bör typen av payload som TS föreslår vara ett nummer. Detta är anledningen till att jag skapade detta: DiceGameMutationPayloadMap, och sedan skapade jag const mutations. Typescript bör tala om vilken typ payload ska ha (baserat på mutationsnamnet).

Ett bra exempel på detta kan också vara att skriva för addEventListenr-metoden som tillhandahålls av TypeScript. Denna typ härleder typen av event (ev) baserat på ett givet evenemangsnamn (type):

addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLButtonElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;

För mer information om denna typ bör du kolla lib.dom.ts → rad 6385

Svar #1

Faktiskt, det är möjligt. Det tar lite tid för mig att lista ut vad jag gör fel men i slutändan förstod jag mitt misstag.

Mitt första misstag var inne i DiceStoreMutation eftersom mitt tredje argument av denna generiska refererade till typen av nyckeln och inte nyckelns namn. Beviset på detta kan vi se på bifogad skärmdump:

Vi kan se att Expanded visar att p faktiskt är en sträng, men det borde vara en union av strängliteral 'ADD_DICE_HISTORY_ITEM' | 'DECREMENT_ROUND' | 'INCREMENT_SCORE'. Man skulle kunna tänka att kanske jag bara borde använda keyof DiceGameMutationsKeys istället för keyof typeof DiceGameMutationsKeys vilket skulle referera till nyckelnamn för detta gränssnitt istället för typer av nycklar, men detta resulterade förstås i ett fel som förväntat. Det fick mig att tänka att jag borde ändra min generiska MutationType på något sätt. Efter att jag läst @Joel Bourbonnais svar och tittat på detta mycket bra exempel om P[K]-delen som en Index types, kom jag på en ny idé för min MutationType: Mapped generics + Index types. Denna nya typ fick denna form:

export type MutationType<T, U> = {
  [K in keyof U]: (state: T, payload: U[K]) => void;
};

Och jag kan bara säga en sak Det fungerar utmärkt. Nu behöver jag inte ens DiceGameMutationsKeys eftersom DiceGameMutationPayloadMap tillhandahåller all information. Du kanske frågar varför? Jag skyndar redan med en förklaring:

  • För det första, K in keyof U så K kan endast vara en nyckel av U så det kommer att vara en union av strängliteraler av U nyckelgränssnitt.
  • För det andra, U[K] så det kommer att vara en sökning efter en typ av U under K-nyckeln (en av denna union av sträng-literals av U-nycklar gränssnitt)

Nedan kan du hitta en skärmdump som bevis:

Relaterade artiklar