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

Union type doesn't match the description in the Handbook #51062

Closed
cnshenj opened this issue Oct 4, 2022 · 5 comments
Closed

Union type doesn't match the description in the Handbook #51062

cnshenj opened this issue Oct 4, 2022 · 5 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@cnshenj
Copy link

cnshenj commented Oct 4, 2022

Bug Report

🔎 Search Terms

union; widen; type

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about union.

⏯ Playground Link

Playground link with relevant code

💻 Code

The Handbook describes the union type:

A union type is a type formed from two or more other types, representing values that may be any one of those types.

This means if we define type T = A | B, then its value can be any of A's values, or any of B's values. It implicitly means that if a value is not of type A or B, it is not a valid T value.

However, the code below shows that the same value is considered not A and not B, but is A | B. This contradicts the Handbook. One of them needs to changed to match the other. To me, the Handbook seems to make more sense.

type A = { a: number };
type B = { b: number };

const a: A = { a: 0, b: 1 }; // Error, "b" not allowed
const b: B = { a: 0, b: 1 }; // Error, "a" not allowed
const ab: A | B = { a: 0, b: 1 }; // No error, although the value is not a valid A or B

🙁 Actual behavior

A value of A | B can be neither A nor B.

🙂 Expected behavior

A value of A | B should be either A or B.

@IllusionMH
Copy link
Contributor

IllusionMH commented Oct 4, 2022

{ a: 0, b: 1 } is valid A, you just get excess properties check for plain object, which won't work for unions.

Looks like duplicate of #20863

Or you'll need exact types #12936 to make { a: 0, b: 1 } invalid A.

@MartinJohns
Copy link
Contributor

MartinJohns commented Oct 4, 2022

Duplicate of #20863.

The expected behavior is the actual behavior. Your value is both A and B, so it's also A | B. Excess property checks are more of a linter feature, but are not involved in the types.

edit: again 30 seconds too late, what's going on?!

@cnshenj
Copy link
Author

cnshenj commented Oct 5, 2022

The explanation makes sense. Although it is not clear from the error that it is "regular" error or "linter" error.
A related question, I'm afraid there is probably already a duplicate but I can't find it:

type A = { a: number };
type B = { b: string };

function isA(value: A | B): value is A {
    return "a" in value;
}

function foo<T extends A | B, K extends keyof T>(value: T, key: K) {
    if (isA(value)) {
        value["a"] + 1; // OK, value["a"] is number
        value[key] + 1; // Error. What type should key be to make this line valid?
    }
}

@fatcerberus
Copy link

Although it is not clear from the error that it is "regular" error or "linter" error.

This error being misleading is one of my pet peeves (it says “not assignable” which is not strictly true when just looking at the types involved), but the full error text does hint at what’s really wrong:

Object literal can only specify known properties

…meaning the assignment was rejected because you tried to assign a fresh object literal with too many properties for the type it’s being assigned to. This check only applies to directly-assigned object literals; a regular type error will not include this text. It is meant to 1) catch typos and 2) prevent you from specifying properties that you’ll immediately lose access to.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Oct 5, 2022
@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

6 participants