Search code examples
typescripttype-inference

Is it possible to modify the inferred type of a literal in TypeScript?


Consider the following code that tries to conditionally add a property to an object with an inferred type:

const foo = {
    a: 1,
    b: 2,
};

if (bar) {
    foo.c = 3; // Error: Property 'c' does not exist on type '{ a: number; b: number; }'.(2339)
}

It's possible to remove the error by explicitly declaring the type of foo as { a: number; b: number; c?: number; } or using a spread to conditionally add c:

const foo = {
    a: 1,
    b: 2,
    ...(bar ? { c: 3 } : {}),
};

However, let's say we wanted to keep the original code structure but we also wanted to avoid having to explicitly declare properties that could be inferred. Are there any solutions that could satisfy both counts? For example, is it possible to somehow adjust the inferred type, something like:

const foo = {
    a: 1,
    b: 2,
} as { ...; c?: number; }; // Example, does not work

Solution

  • This isn't pretty, but it works: the property types for a and b are inferred, and don't have to be redundantly declared.

    function withMissingProps<T extends {}>() {
      return function<S extends {}>(obj: S): S & Partial<T> {
        return obj;
      }
    }
    
    const foo = withMissingProps<{ c: number }>()({
      a: 1,
      b: 2
    });
    
    if(Math.random() > 0.5) {
      foo.c = 1;
    }
    

    There are two type parameters, T and S, for the declared and inferred properties respectively. Unfortunately if a function has two type parameters then you must either provide both or infer both; the solution is to curry the function, though this means an extra pair of brackets.

    Playground Link