Trying to override a variable inside a function and get an error: "Block-scoped variable '...' used before its declaration.ts(2448)"
Asked Answered
A

3

6

I'm writing a typescript function that accepts a numeric array (i.e., type: number[]) and calculates its mean. In addition, I want to account for when the input array might contain some null values. To this end, I added an argument, that when set to true, tells the function to remove nulls before calculating the mean.

But I can't figure out the proper way to do this, as I can't override the input within the function.

Here's my code for calcMean()

function calcMean(arr: number[], nullRemove: boolean = true): number {
    if (nullRemove) { // if TRUE, which is the default, then throw out nulls and re-assign to `arr`
        const arr: number[] = arr.filter((elem) => elem !== null);
    }
    // then simply calculate the mean of `arr`
    return arr.reduce((acc, v, i, a) => acc + v / a.length, 0); // https://mcmap.net/q/100282/-how-to-compute-the-sum-and-average-of-elements-in-an-array-duplicate
}

I then get an error:

Block-scoped variable 'arr' used before its declaration.ts(2448)

I also tried using let in addition or instead of const but it didn't solve the problem.

What am I missing here?

Apply answered 3/3, 2022 at 9:0 Comment(2)
What should happen when nullRemove is false?Wretched
@Wretched if nullRemove is false then the IF block shouldn't be executed, thus return arr.reduce((acc, v, i, a) => acc + v / a.length, 0); is the only thing the function does.Apply
A
5

Two options for you:

1. Don't redeclare it, just reassign it:

function calcMean(arr: number[], nullRemove: boolean = true): number {
    if (nullRemove) { // if TRUE, which is the default, then throw out nulls and re-assign to `arr`
        arr = arr.filter((elem) => elem !== null);
        // ^^^ No `const` here
    }
    // then simply calculate the mean of `arr`
    return arr.reduce((acc, v, i, a) => acc + v / a.length, 0); // https://mcmap.net/q/100282/-how-to-compute-the-sum-and-average-of-elements-in-an-array-duplicate
}

Some folks believe reassigning parameters is poor style (I'm not one of them provided the function is quite small as in your case, but I understand the argument), so alternatively:

2. Assign to a different variable:

function calcMean(arr: number[], nullRemove: boolean = true): number {
    // Remove `null` if requested
    const a = nullRemove ? arr.filter(elem => elem !== null) : arr;
    // then simply calculate the mean of `arr`
    return a.reduce((acc, v, i, a) => acc + v / a.length, 0); // https://mcmap.net/q/100282/-how-to-compute-the-sum-and-average-of-elements-in-an-array-duplicate
}
Aeschines answered 3/3, 2022 at 9:5 Comment(2)
I like that you addressed the topic of parameter immutability. Also: using inverted ternary conditionals objectively increases cognitive load when reading source code.Wretched
@Wretched - Re the inversion: The purpose there was to avoid making it hard to find the arr at the end. But sure, it could absolutely be const a = nullRemove ? arr.filter(elem => elem !== null) : arr; I waffled on it when writing it. You're probably right I should swap it around.Aeschines
W
1

The code in your question doesn't currently allow for null values to be included in the number[] parameter.

I like for my code to be explicit about what's happening, so in the case where there might be nulls mixed in the array of numbers, I would explicitly convert them to 0 in the case that they are not removed:

TS Playground

/** Each null element is either omitted or converted to 0 */
function handleNullValues (arr: readonly (number | null)[], omit = true): number[] {
  return omit ?
    arr.filter((value): value is number => value !== null)
    : arr.map(value => value === null ? 0 : value);
}

function calcMean (arr: readonly (number | null)[], nullRemove = true): number {
  const numbers = handleNullValues(arr, nullRemove);
  return numbers.reduce((sum, n) => sum + n) / numbers.length;
}


// Test
console.assert(calcMean([null, 1, 3, 7, 9]) === 5); // ok
console.assert(calcMean([null, 10, 1, 3, 7, 9], false) === 5); // ok


Edit: Updated in response to your comment.

TS Playground

function calcMean (arr: readonly (number | null)[]): number {
  let count = 0;
  return arr.filter((value): value is number => {
    const isNumber = typeof value === 'number';
    if (isNumber) count += 1;
    return isNumber;
  }).reduce((sum, n) => sum + n) / count;
}


// Test
console.assert(calcMean([1, 3, 7, 9]) === 5); // ok
console.assert(calcMean([null, 1, 3, 7, 9]) === 5); // ok
Wretched answered 3/3, 2022 at 10:14 Comment(2)
Thanks. Converting null to 0 is mathematically problematic because those 0s are still counted in the denominator. Thus, dropping null before calculation is the only sensible pre-step.Apply
@Apply In that case (null values should always be removed), the additional argument is not necessary since there is no alternate logic branch needed. See my updated answer.Wretched
C
1

In my case for typescript, I also received the message

Block-scoped variable 'filename' used before its declaration.ts(2448)

exportAsExcel () {
   const format = 'xlsx'
   const exportSelectedOnly = true
   const filename = 'test'
   this.$refs.grid.exportTable(format, exportSelectedOnly, filename)
}

Solved by

const format = 'xlsx' as string
const exportSelectedOnly = true as boolean
const filename = 'test' as string
Cattegat answered 5/1, 2023 at 3:15 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.