I am facing an issue with TypeScript's strictNullChecks setting. I have a function handleAction which expects an argument of type MyType.
type MyType = {
prop: MyEnum;
// ... other properties
};
enum MyEnum {
Value1,
Value2,
// ... other values
}
async function handleAction(item: MyType) {
// Function logic here
}
Now, I have an object myRecord which has a property nestedItem that can be null.
type MyRecord = {
nestedItem: MyType | null;
// ... other properties
};
const myRecord: MyRecord = {
nestedItem: null, // or some MyType object
// ... other properties
};
I want to check if nestedItem exists and if its prop is Value1. I tried two approaches:
Approach 1:
const isCertainCondition =
myRecord.nestedItem &&
myRecord.nestedItem.prop === MyEnum.Value1;
if (isCertainCondition) {
await handleAction(myRecord.nestedItem); // Error here
}
Approach 2:
if (
myRecord.nestedItem &&
myRecord.nestedItem.prop === MyEnum.Value1
) {
await handleAction(myRecord.nestedItem); // No error here
}
The first approach gives me a TypeScript error:
Argument of type 'MyType | null' is not assignable to parameter of type 'MyType'.
Type 'null' is not assignable to type 'MyType'
The second approach works without any error.
Why is TypeScript behaving differently between these two approaches?
The reason why you get different results here is Narrowing | TypeScript Handbook
When you have this type of an example, you use "Truthiness narrowing" to exclude
null:Same happens here but you keep the narrowed information in the variable
nestedItem:When you keep the check in the variable
nestedItemand try to accessmyRecord.nestedItem, you lose the narrowed information because it's only located in the variablenestedItem:Although you can mitigate this by creating type predicate, e.g.
isDefined:So instead of truthiness check you can use type predicate:
Let me know if you have more questions
TypeScript playground with all examples step by step - https://tsplay.dev/m0V4rw