Búsqueda de sitios web

typescript - Tipando correctamente el parámetro getters de un getter en vuex


¿Cómo escribiría correctamente un getter de Vuex que se refiere a otros getters? Por ejemplo:

import { GetterTree } from 'vuex';

class State {
    foo: string[] = [];
}

const getters = <GetterTree<State, any>>{
    bar: state => state.foo.filter(...something...),
    baz: (state, getters) => getters.bar.filter(...something else...)
}

Aquí el parámetro getters de getters.baz está tipado como any por la definición de vuex. Sería agradable si pudiera ser tipado como { bar: string[] } implícitamente basado en la declaración de const getters, sin tener que declararlo dos veces como una interfaz o algo así. ¿Hay alguna manera de hacer eso?

Respuesta #1

Echemos un vistazo a la interfaz GetterTree:

export interface GetterTree<S, R> {
  [key: string]: Getter<S, R>;
}

Por lo tanto, state => state.foo.filter(...algo...) corresponde a Getter<S, R>.

Desafortunadamente, el segundo argumento getters no es genérico y se tipa por defecto como any:

export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;

El parámetro genérico R del GetterTree corresponde a rootState, que no nos resulta interesante.

Veo dos opciones.

Primero - lo más fácil.

Solo agrega un tipo explícito al segundo argumento:

const getters: GetterTree<State, any> = {
  bar: state => state.foo.filter(e => e.length > 1),
  baz: (state, getters: { bar: string[] }) => getters.bar
}

El segundo es sobrescribir el tipo de utilidad incorporado GetterTree.

import { GetterTree, Getter } from 'vuex';

class State {
  foo: string[] = [];
}

type CustomGetter<T extends Getter<any, any>, Getters> =
  T extends (state: infer State, getters: infer _, rootState: infer RootState, rootGetters: infer RootGetters) => any
  ? (state: State, getters: Getters, rootState: RootState, rootGetters: RootGetters) => any
  : never

type CustomGetterTree<T extends GetterTree<any, any>, Getters> = T extends GetterTree<infer S, infer R> ? {
  [key: string]: CustomGetter<Getter<S, R>, Getters>;

} : never

const getters: CustomGetterTree<GetterTree<State, any>, { bar: string[] }> = {
  bar: state => state.foo.filter(e => e.length > 1),
  baz: (state, getters /** {  bar: string[];} */) => getters.bar
}

Parque infantil

Respuesta #2

Editar: si estás usando Vue3 y TypeScript con Vuex, cambiaría a Pinia, que parece ofrecer una seguridad de tipos decente. Funcionó para mí, al menos.

No sé si vale la pena considerar un enfoque diferente. Me gusta intentar inferir el tipo de retorno de un getter usando una función auxiliar. Encuentro que me ahorra en las definiciones de tipos complejas, aunque implica un poco de repetición en las claves utilizadas para referirse a un getter.

const getters = {
    gridHeight(): number {
        return 32;
    },
    message(): string {
        return 'hello 👋';
    },
};
const store = createStore<State>({ getters });

type ReturnOfGetter<T> = T extends (state: infer _, getters: infer _, rootState: infer _, rootGetters: infer _) => infer Return
    ? Return                                                                                                                          
    : never;

export function useGetter<K extends keyof typeof getters>(key: K): ReturnOfGetter<typeof getters[K]> {
    return store.getters[key];                                                                                           
};                                                                                                                       

Esto permite el acceso de la siguiente manera:

const gridHeight = useGetter<'gridHeight'>('gridHeight');
const message = useGetter<'message'>('message');