You cannot reference an alias other than ORDER BY, because SELECT is the second last evaluated clause. Two workarounds:
SELECT BalanceDue FROM ( SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue FROM Invoices ) AS x WHERE BalanceDue > 0;
Or just repeat the expression:
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue FROM Invoices WHERE (InvoiceTotal - PaymentTotal - CreditTotal) > 0;
I prefer the latter. If an expression is extremely complex (or expensive to calculate), you should probably consider a computed column (and possibly a persistent one), especially if many queries relate to the same expression.
PS Your concerns seem unfounded. In this simple example, at least SQL Server is smart enough to execute only once, even if you referenced it twice. Go ahead and compare plans; you will see that they are identical. If you have a more complicated case, when you see an expression evaluated several times, send a more complex request and plans.
Here are five sample queries that give the exact same execution plan:
SELECT LEN(name) + column_id AS x FROM sys.all_columns WHERE LEN(name) + column_id > 30; SELECT x FROM ( SELECT LEN(name) + column_id AS x FROM sys.all_columns ) AS x WHERE x > 30; SELECT LEN(name) + column_id AS x FROM sys.all_columns WHERE column_id + LEN(name) > 30; SELECT name, column_id, x FROM ( SELECT name, column_id, LEN(name) + column_id AS x FROM sys.all_columns ) AS x WHERE x > 30; SELECT name, column_id, x FROM ( SELECT name, column_id, LEN(name) + column_id AS x FROM sys.all_columns ) AS x WHERE LEN(name) + column_id > 30;
The final plan for all five queries:

Aaron Bertrand Jun 25 2018-12-12T00: 00Z
source share