Skip to content
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

Inconsistent boolean literal types #48363

Closed
ahejlsberg opened this issue Mar 21, 2022 · 2 comments Β· Fixed by #48380
Closed

Inconsistent boolean literal types #48363

ahejlsberg opened this issue Mar 21, 2022 · 2 comments Β· Fixed by #48380
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@ahejlsberg
Copy link
Member

ahejlsberg commented Mar 21, 2022

πŸ•— Version & Regression Information

This is the behavior in every version I tried.

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

// Box<T> type is invariant in T

type Box<T> = {
    get: () => T,
    set: (value: T) => void
}

declare function box<T>(value: T): Box<T>;

const bn1 = box(0);  // Box<number>
const bn2: Box<number> = box(0);  // Ok

const bb1 = box(false);  // Box<boolean>
const bb2: Box<boolean> = box(false);  // Error, box<false> not assignable to Box<boolean>

πŸ™ Actual behavior

Error on bb2 is surprising and inconsistent with lack of error on bn2.

πŸ™‚ Expected behavior

Expected the box(false) expression to have type Box<boolean> for both bb1 and bb2.

@MartinJohns
Copy link
Contributor

Related: #48150

@ahejlsberg
Copy link
Member Author

The issue here is that when a boolean literal (false or true) is contextually typed by type boolean, we don't widen the type of the literal to boolean. This is inconsistent with our behavior for string, number and bigint literals which are widened when their contextual type is string, number or bigint, respectively. The difference comes from the fact that boolean is just an alias for the union type false | true, whereas string, number, and bigint aren't (infinite) unions of literals, but rather distinct primitive types.

This issue was discussed in #48150 and before that here. It definitely seems odd and inconsistent. We'd be better off considering the contextual type boolean akin to string, number, and bigint for purposes of widening literals. I know we tried that in the past and one issue was a boolean contextual type arising from separate false and true types as discriminants. For example:

let x: { kind: false, x: number } | { kind: true, x: string } = { kind: false, x: 42 };

Here, at least in the past, the kind property in the object literal would have the contextual type boolean (the union of false and true). However, these days we discriminate the contextual type by discriminant properties in the object literal, thus removing the { kind: true, x: string } variant, and therefore the contextual type is just false. And thus we wouldn't widen.

I'm going to put up a PR to see what the effects are.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
3 participants