Home > front end >  Typescript config builder partial with variable number of generics for child types
Typescript config builder partial with variable number of generics for child types

Time:09-05

I'm trying to right a type safe config builder for partial types. It'll be handy for setting and overriding default params for testing.

I have this so far:

const getPartial = <T>(defaultParameters: Partial<T>) =>
    (overrideParameters: Partial<T>): Partial<T> => ({
        ...defaultParameters,
        ...overrideParameters
    });

Which works fine for simple types such as:

interface IMessage {
    id: number
    message: string
}

const getMessage = getPartial<IMessage>({
   id: 1,
   message: 'Hello'
});

console.log(getMessage({
    message: 'Hi'
}));

But if I have a child type with another generic, or multiple generics, I'm not sure the best pass that down the chain.

interface IWithGeneric<T extends {}> {
    id: number
    data: Partial<T> // Need to set as Partial to avoid "Type '{}' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to '{}'." error
}

// Need to curry the function again to have something to hook the generics on. 
const getWithGeneric = <T>() => getPartial<IWithGeneric<T>>({
    id: 1,
    data: {},
});

console.log(getWithGeneric<{ message: string }>()({
    id: 1,
    data: {
        message: 'hello',
    }
}));

Is there a way I can pass through multiple variable generic types without currying the function and/or without needing a Partial? Would really appreciate it if someone can point me in the right direction.

CodePudding user response:

As kelly mentioned, it doesn't look like it's possible to pass variable generics down like that.

It might be more palatable if you name the curried function, it'll be much more readable anyway.

interface IWithGeneric<T extends {}> {
    id: number
    data: Partial<T>
}

const getWithGeneric = <T>() => getPartial<IWithGeneric<T>>({
    id: 1,
    data: {},
});

const getWithMessage = getWithGeneric<{ message: string }>();

console.log(getWithMessage({
    id: 1,
    data: {message: 'hello'}
}));

With multiple generics you'd have something like this:

const getPartial = <T>(defaultParameters: Partial<T>) =>
    (overrideParameters: Partial<T>): Partial<T> => ({
        ...defaultParameters,
        ...overrideParameters
    });

interface IWithMultipleGenerics<A extends {}, B extends {}> {
    id: number
    data: Partial<A>,
    meta: Partial<B>
}

const getWithMultipleGenerics = <A, B>(defaultData: IWithMultipleGenerics<A, B>) => 
    getPartial<IWithMultipleGenerics<A, B>>(defaultData);

const getWithMessageAndPage = getWithMultipleGenerics<{ message: string }, {page: number}>({
    id: 1,
    data: {message: 'hello'},
    meta: {page: 1}
});

console.log(getWithMessageAndPage({
    id: 1,
    data: {message: 'hello'},
}));
  • Related