I am currently working on this question called '
What I don't understand is, I clearly covered every case and TS should be guessing that root2 can't be null at the point where it's yelling 'root2 is possibly null'.
Am I missing any case or it's just me not understanding how the compiler behave..?
It would be much appreciated if you could point me in the right direction.
My solution
const mergeTrees = (
root1: TreeNode | null,
root2: TreeNode | null
): TreeNode | null => {
if (!root1 && !root2) return null;
if (root1 && root2)
return new TreeNode(
root1.val root2.val,
mergeTrees(root1.left, root2.left),
mergeTrees(root1.right, root2.right)
);
if (root1 && !root2)
return new TreeNode(
root1.val,
mergeTrees(root1.left, null),
mergeTrees(root1.right, null)
);
return new TreeNode(
root2.val,
mergeTrees(null, root2.left),
mergeTrees(null, root2.right)
);
}
CodePudding user response:
There are limits to the flow analysis that TypeScript's compiler does. Although you (and we) know from the code logic that root2
can't be null
there at the end, TypeScript doesn't.
You have at least three options:
- Use a non-nullish type assertion on
root2
- Write an explicit test that throws an assertion error if
root2
isnull
, inline in that function - Use a type assertion function
Use a non-nullish type assertion
The !
operator at the end of a value expression is an assertion saying "I know this value will never be null
or undefined
('nullish')." So you could use it in all three places:
return new TreeNode(
root2!.val,
mergeTrees(null, root2!.left),
mergeTrees(null, root2!.right)
);
or in just one place, by assigning to a new identifier:
const r = root2!;
return new TreeNode(
r.val,
mergeTrees(null, r.left),
mergeTrees(null, r.right)
);
Like all type assertions, if you get your logic wrong, TypeScript won't be able to help you because it will believe what you tell it. So if the logic is wrong, you'll get an unexpected error accessing root2
when it's nullish.
Explicit inline test of root2
You could also add a runtime check and throw an assertion error:
if (!root2) {
throw new Error("Assertion failure, 'root2' can't be nullish here");
}
return new TreeNode(
root2.val,
mergeTrees(null, root2.left),
mergeTrees(null, root2.right)
);
That has the advantage of both testing your assertion and providing a clear error about it, and reassuring TypeScript's compiler.
A type assertion function
This is pretty much the same as above, but in a reusable package. You can write a reusable type assertion function that tests for nullish values and throws the error:
// In some utilities module...
function assertIsNotNullish<T>(value: T | null | undefined): asserts value is T {
if (value === null || value === undefined) {
throw new Error("Value shouldn't be nullish here");
}
}
// In your mergeTrees function:
assertIsNotNullish(root2);
return new TreeNode(
root2.val,
mergeTrees(null, root2.left),
mergeTrees(null, root2.right)
);
This has the same advantage that it both tests the assertion and also reassures the compiler.
CodePudding user response:
You could shorten your code with nullish coalescing and ternaries, which should also allow the compiler to resolve types
const mergeTrees = (
root1: TreeNode | null,
root2: TreeNode | null
): TreeNode | null => {
if (!root1 && !root2) return null;
else {
return new TreeNode(
(root1.val ?? 0) (root2.val ?? 0),
mergeTrees(root1 ? root1.left : null, root2 ? root2.left : null),
mergeTrees(root1 ? root1.right : null, root2 ? root2.right : null)
);
}
}
CodePudding user response:
I'm not familiar with typescript, but I can help in the right direction: In C# there's a case where you can have nullable variables and non-nullable variables. and usually before you're able to use these nullable variables, you have to make them non-nullable instead.
As example in C#:
DateTime? date = null //nullable variable
int seconds = 0;
date = DateTime.Now();
if (date != null)
{
//it's ensured that date is not null, but this still won't compile,
//because it's still a nullable variable
seconds = date.Second;
//instead, it should be converted to a non-nullable variable. In C#, this goes with '.Value'.
seconds = date.Value.Second;
}