-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handling void by nullish coalescing operator #40613
Comments
This is working as intended (see #26234). Imagine: ffunction func(): any {
const d = Math.random();
if (d < 0.25)
return 1;
if (d < 0.5)
return "-";
if (d < 0.75)
return { a: 1};
return 0;
}
type PossiblyVoid = () => void | boolean;
let empty : PossiblyVoid = func;
let coalesced : boolean = empty() ?? true; The declared |
Expanding on what @j-oliveras said: A function returning If you intend to have a "nothing" value you should use |
All right, I get it now. The problem I have with |
Actually, I found an issue that describes this: #36288 |
In that case, how come the ternary and or operators work? function func(): any {
const d = Math.random();
if (d < 0.25)
return 1;
if (d < 0.5)
return "-";
if (d < 0.75)
return { a: 1};
return 0;
}
type PossiblyVoid = () => void | boolean;
let empty : PossiblyVoid = func;
const emptyReturn = empty();
let nullishCoalesced : boolean = emptyReturn ?? true; // error
let orCoalesced : boolean = emptyReturn || true; // ok
let ternaryCoalesced: boolean = emptyReturn ? emptyReturn : true; // ok |
@RyanCavanaugh Can this be re-opened for discussion? The behavior of the nullisch coalesce vs if for type narrowing with
Actually writing a void-returning function that has any value other than function testOne(): void {
return 1; // type 'number' is not assignable to type 'void'
}
function testTwo(): void {
console.log('void');
} // returns 'undefined'
function testThree(): void {
return undefined;
} // returns 'undefined'
function testFour(): void {
return;
} // returns 'undefined'
function testFive(): boolean | void {
return;
} // returns 'undefined'
function testSix(): boolean | void {
return true;
} // returns '<boolean> true'
function testSeven(): boolean | void {
return 1; // type '1' is not assignable to type 'boolean | void'
} The only way I can figure out to make it happen is: function test(cb: () => any): void {
return cb();
}
function callback(): boolean {
return true;
}
test(callback); This just kind of disables the type check, and since we disallow Barring the example with -- I found this issue while searching information about void and the nullish coalesce. We use an explicit // db library function
export const selectOne = <T>(query: { query: string, replacements: number[] }): Promise<T | void> => {
const res = await dbPool.query(query, replacements);
const rows = res.rows;
if (!rows.length) {
return undefined;
}
if (rows.length > 1) {
throw new Error('Multiple rows returned');
}
return rows[0];
};
// service function
export const getRowByPrimaryKey = (id: number): Promise<SomeRecord | void> => {
const db = getDb();
const query = getQuery(id);
return db.selectOne<SomeRecord>(query.query, query.replacements);
}; Consumer example 1: // the `| void` forces the developer to think about what to do if nothing is returned
// and indicates that the function purposely returns undefined in some scenarios
const record: SomeRecord | void = await getRowByPrimaryKey(1);
if (!record) {
console.log('No record returned');
return;
}
// this works because after the if, TypeScript narrows `record` to the `SomeRecord` type, dropping the `void`
const { prop } = record; Consumer example 2: const record: SomeRecord | void = await getRowByPrimaryKey(1);
// this does not work - 'Property 'prop' does not exist on type 'void | SomeRecord | { prop: number }'
const { prop } = record ?? { prop: 1 };
// you can force it by explicitly typing
const { prop } = <SomeRecord> record ?? { prop: 1 }; So oddly the Of course using |
The example works with
Frankly, then you're abusing |
Indeed, we are abusing function isPositive(input: number): true | undefined {
if (input < 1) {
return undefined;
}
return true;
}
// val is type `true`
const val = isPositive(-1); Adding the const bool: true = undefined; There's some inconsistency in the behavior for void. It seems TypeScript only has a problem testing for truthiness if the type is simply const maybeVoid = (): void | number => Math.random() < 0.5 ? alwaysVoid(callback) : 0;
const alwaysVoid = (cb: () => void): void => cb();
const callback = (): boolean => true;
function try1() {
const rec = alwaysVoid(callback);
if (!rec) { // An expression of type 'void' cannot be tested for truthiness
return;
}
}
function try2() {
const rec: number | void = maybeVoid();
if (!rec) {
console.log('number | void, but actually can only be number here', rec);
return;
}
console.log('number, but actually can only be void here', rec);
} In |
It makes a difference if you enable |
TypeScript Version: 4.0.2
Search Terms: nullish coalescing, void
Code
Expected behavior:
Should not throw a compilation error
Actual behavior:
Nullish-coalescing operator does not treat
void
variables as undefined, even though in JS a function that does not return anything is the same as the one returningundefined
.I am not entirely sure this is a bug, but intuitively it feels like is should behave in a different way. If not, could you please shed some light on the reasons behind it?
Playground Link:
https://www.typescriptlang.org/play?ts=4.0.2#code/C4TwDgpgBACg9gZwQSwEYBsQDU7ICZQC8UAFAJREB8UAbrgQD5SpxzoQCGAdgNwBQ7YFAgBbMKCgAuWIhQZs9IqQqFqAbygBfARCEBjOB3YI9EAtJZtOXJaPEhyUAPxOowAE4BXCPyA
The text was updated successfully, but these errors were encountered: