typescript - Apelarea funcțiilor obiectelor dinamice de tip


Am acest cod (Playground):

const routes = {
    projects: ({}) => "/projects",
    "projects.edit": ({ id }: { id: string }) => `/projects/${id}`,
    report: ({ projectId }: { projectId: string }) => `/report/${projectId}`,
};

type Routes = typeof routes;

export function generateUrl<Name extends keyof Routes>(
    name: Name,
    params: Parameters<Routes[Name]>[0]
): string {
    const fn = routes[name];
    return fn(params);
}

Primesc această eroare la linia fn(params). Cum aș putea să o scriu pentru a verifica tipurile (fără a folosi any)?

Proprietatea 'id' lipsește din tipul '{ projectId: string; }', dar este obligatorie în tipul '{ id: string; }'.

Răspuns #1

Iată o altă soluție
Aceasta vă permite să aveți rute care iau mai mulți parametri.

type Route = (...args: any[]) => string;
type Routes = {
    [index: string]: Route;
};

function createUrlGenerator<T extends Routes>(router: T) {
    return <K extends keyof T>(name: K, ...params: Parameters<T[K]>): string => {
        return router[name].apply(null, params);
    }
}

const routes = {
    projects: ({}) => "/projects",
    "projects.edit": ({ id }: { id: string }) => `/projects/${id}`,
    report: ({ projectId }: { projectId: string }) => `/report/${projectId}`,
    multyParams: (id: number, query: string) => `${id}/${query}`
};

export const generateUrl = createUrlGenerator(routes);

generateUrl('multyParams', 123, '43');
generateUrl('multyParams', 123); // Exception
generateUrl('projects.edit', { id: '123' });
generateUrl('projects.edit', { id: 123 }); // Exception

Răspunsul #2

const routes = {
    projects: ({}) => "/projects",
    "projects.edit": ({ id }: { id: string }) => `/projects/${id}`,
    report: ({ projectId }: { projectId: string }) => `/report/${projectId}`,
};

type Routes = typeof routes;

export function generateUrl<Name extends keyof Routes>(
    name: Name,
    params: Parameters<Routes[Name]>[0]
): string {
    const fn = routes[name] as ((p: typeof params => ReturnType<Routes[Name]>);
    return fn(params);
}

Vedeți terenul de joacă.

Practic, problema este că atunci când se preia funcția din routes, TS vrea să fie extrem de sigur și obligă ca valoarea preluată să fie apelată cu intersecția tuturor tipurilor posibile de parametri, sau {} & {id: string} & {projectId: string} în acest caz, ceea ce se traduce prin {id: string, projectId: string}.

Acest lucru nu este evident dorit aici, așa că folosim o conversie sigură din punct de vedere al tipului1.

Rețineți totuși că există un dezavantaj. Prezența rutei 'projects' fără niciun parametru face ca următoarea linie să fie acceptabilă:

fn(42)

Dacă acesta ar fi o problemă, eliminarea rutei goale ar rezolva-o.

1: Încearcă să înlocuiești typeof params cu number.