Search code examples
typescripttypesinterfaceoption-typedestructuring

TypeScript destructured default parameter with type or interface


I'm writing a function that takes an optional object options, itself containing an optional property params. This options object has a default value {} so it can be properly destructured in the function signature.

However, I'm encountring issues when trying to type it with an interface:

type Params = {
  params?: { [key: string]: boolean }
}

interface Foo {
  (options?: Params): void
};

const myFoo: Foo = ({ params } = {}) => {} // Property 'params' does not exist on type 'Params | undefined'.

The error makes sense: as far as the compiler knows, options may be undefined (as interpreted by it when a parameter is set as optional), so params may not exist on it.

However, it doesn't take into account that an optional parameter may have a default value. Yet, I haven't found a way to indicate this properly in a type or an interface. Typing directly in the function signature does work, but this can't be reused or exported.

Is there any standard way to work this out while using a type or an interface?


Solution

  • The type inference for the options parameter within the parameter list via myFoo: Foo is Params | undefined, regardless of whether a default value is provided in the implementation. Only in the body of myFoo does it infer options as Params because of the default value:

    // hover over options here: Params | undefined
    const myFoo: Foo = (options = {}) => {
      // hover over options here: Params
      options;
    };
    

    To fix it, defer the object destructuring until the type for options is inferred completely from the implementation's default value:

    // no error
    const myFoo: Foo = (options = {}) => {
      const { params } = options;
    };
    

    Or as you've already discovered, you can help the type inference by explicitly typing the parameter as Params. This carries the potential risk of misinforming the compiler under certain circumstances though:

    // no error
    const myFoo: Foo = ({ params }: Params = {}) => {};