Closed
Description
Bug Report
🔎 Search Terms
undefined type guard narrowing fails
🕗 Version & Regression Information
- Between 4.2.3 and 4.3.5, the narrowing took effect on the second call to takesArray(), which previously reported a similar error.
- The error message changed between 4.7.4 and 4.8.4.
- This appears related to
typeof ... === "undefined"
check on discriminated union ofundefined
and object type doesn't narrow correctly #50340, which @a-tarasyuk may have fixed in fix(50340): typeof ... === "undefined" check on discriminated union of undefined and object type doesn't narrow correctly #50344 (Aug. 31 ’22), but I’m still seeing the issue on the latest nightly (v5.0.0-dev.20230126) so I don't think it's a duplicate. - I also don't think this is a duplicate of Narrowing with
typeof
and optional chaining does not work specifically with a union containingboolean
unless wrapped in parentheses #51700 fixed in Fix narrowing bytypeof
applied to discriminant property #51720 (Dec. 5 '22) as that focuses on the optional chaining operator.
⏯ Playground Link
Playground link with relevant code
💻 Code
type MappedType = {[T in ('Alpha' | 'Beta')]: {[index: string] : number[]};}
declare function takesArray(arg0: number[]) : boolean;
export const demoFn = function<T extends keyof MappedType>(
key: T, //clarifying that this is known/specified
row: Partial<MappedType[T]>
) {
for(let attribute in row) {
const attributeValue = row[attribute];
//attributeValue could be undefined here, that's OK
takesArray(attributeValue); //err as expected
//Use of this syntax required by https://eslint.org/docs/latest/rules/no-undefined
//but the error doesn't appear when using `if(attributeValue !== undefined) {`
if(typeof attributeValue !== 'undefined') {
takesArray(attributeValue); //attributeValue is number[]
// with undefined narrowed out, but next line has error saying
//'attributeValue' is possibly 'undefined'. ts(18048)
for(let member of attributeValue) {
console.log('This array includes '+member+'.');
}
}
}
}
🙁 Actual behavior
Error 'attributeValue' is possibly 'undefined'
(for the purposes of a for loop but not for the purposes of a function call) inside a type-guard conditional checking for undefined, where attributeValue is a constant originally copied from a mapped object type.
🙂 Expected behavior
- Only the first error, on the line with the comment "err as expected".
- I would also expect TS to be consistent about whether
attributeValue
can be undefined within the two immediate children lines in the conditional (function call and for loop). - I would also expect TS to be consistent between the use of
typeof attributeValue !== 'undefined'
andattributeValue !== undefined
. - I would also expect TS to behave consistently whether the same nominal type originally comes from a mapped type or not. Note that the sample code above uses a mildly complex derived type, which is still much simpler than the inspiring example, and the motivation for this was left out of the example to get it closer to minimal with focus on the failing type guard. The "workaround" of using simpler non-derived types would NOT solve the issue here.