0

I'm new to TypeScript and struggling trying to create a function which properly sets default values for an optional object inside an object.

When I try to define everything in the parameters, I'm getting an error that one of the properties (options.target.value) may be undefined. The object in question may be optionally provided when the function is invoked and constrained to an interface that requires the property, or if the object is not provided will be set using a function which also uses the same interface.

What's confusing is that I'm providing a default options.target that isn't satisfying TypeScript, but when I check !options.target and provide it using the same getTarget() function, TypeScript is happy. Is this just a bug in TypeScript, or am I misunderstanding something about how default object properties are set?

Thank you!

function getTarget(): Target {
    const target: Target = page.targets[0];
    console.log('target._id = ', target._id); //always OK, TS does not mark this as possibly undefined.
    return target;
}


function test(
    options: {target?: Target;} //when provided, must have a 'value' property
           = {target: getTarget()} //if not provided, default always has a 'value' property
) {
    if (!options.target) { options.target = getTarget(); } //without this, I get the TS error below
    console.log('options.target', options.target); //always OK
    console.log('options.target', options.target.value); //ERROR: TS2532 Object is possibly 'undefined' if required line above commented out
}

4 Answers 4

1

Providing default arguments for nested values can be a bit tricky.

Lets break down what this function signature means:

function test(
    options: { target?: Target } = { target: getTarget() }
)

In plain english, test accepts a single optional argument options. options is an object that has one optional property named target. If the argument is omitted then a default value will be used that has a target for sure.

This means the following function calls would be allowed:

test() // default object used
test({}) // default object not used, target omitted
test({ target: someTarget }) // default object not used, target present.

And it's the test({}) case that would leave target undefined in your function, because you have not provided a target, and you have prevented the default argument from being used.

The fix here would be to make the target not optional:

function test(
    options: { target: Target } = { target: getTarget() }
) {
    console.log('options.target', options.target.value);
}

test() // works
test({ target: { _id: 123, value: 'test' }}) // Works

test({}) // Error

Or to not use default arguments at all and just do it yourself.

function test(
    options?: { target?: Target }
) {
    const target = options?.target ?? getTarget()
    console.log('options.target', target.value);
}

test() // Works
test({ target: { _id: 123, value: 'test' } }) // Works
test({}) // Works

Playground

Sign up to request clarification or add additional context in comments.

Comments

0

What you're attempting to achieve would work if options was not an object. TypeScript is warning you because someone could in theory do this:

test({ });

Which would result in options.target being undefined.

To resolve this issue, you can keep the if approach while removing the default value in the function declaration.

1 Comment

But wouldn't the "Target" interface screen that out?
0

Yes, it's showing that error because you are defining the test function argument as optional. Basically the target?: Target means target: Target | undefined.

What you need to do is either removed the 'question mark' or still check for undefined target.

6 Comments

Not really helpful as OP is trying to assign a default value to prevent it from being undefined while keeping the optional syntax.
Yeah, OP declared it as optional but wants to add a default value. So it's either choose one, either it's optional or set a default value. So if it's optional, then don't add a default value. Then handle the undefined inside the function.
I'm trying to write is so that the variable may be provided or otherwise set to a default value, so removing the question wouldn't work. But when that input is undefined, the default value takes over and makes it defined, right?
If you declare a paramter as optional, for example function Run(speed?: number) then you can call that function either with or without a parameter, like, Run() or Run(10). I think the above example function would translate to this, function Run(speed: number | undefined). So, even if you set a default value to a variable, but the variable has a data type of undefined or null, you still need to check for it or you'll get an exception.
If you set a paramter as optional, giving it a default value might not be the best way. You can also set a default value, for example, function jump(height: number = 50), then either you call the function with, jump() or jump(50).
|
0

Alternative syntax that TS is happy with:

function test(
    {
        target = getTarget(),
    }: {
        target?: Target,
    } = {},
) {
    console.log('target', target);
    console.log('target.value', target.value);
}

Comments

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.