4

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?

0

1 Answer 1

8

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 = {}) => {};
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, this makes a lot of sense. I don’t know if the compiler could ever infer from the signature, but it seems unlikely so in those cases where everything is optional, I guess destructuring in the signature isn’t an option.
It looks like this inference issue was fortunately fixed in TypeScript 3.9.7. If you open this example and switch to an earlier version (i.e. 3.8.3), you'll see that the error is flagged once again: typescriptlang.org/play?ts=3.9.7#code/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.