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

Incorrect type inference when get/set accessors use different types #51229

Closed
otomad opened this issue Oct 19, 2022 · 7 comments
Closed

Incorrect type inference when get/set accessors use different types #51229

otomad opened this issue Oct 19, 2022 · 7 comments
Labels
Duplicate An existing issue was already created

Comments

@otomad
Copy link

otomad commented Oct 19, 2022

The properties of an old library allows setting many types, but only getting one type. This is represented by Date.

interface Foo {
    get bar(): Date;
    set bar(value: Date | number);
}

When this object is get or set individually, it will work properly.

const foo = {} as Foo;
const bar = foo.bar; // typeof bar is Date, ok!
foo.bar = 123; // can be set by number, ok!

However, when I set this object literally, it will not work properly.

const foo: Foo = { bar: 123 }; // The type "number" cannot be assigned to the type "Date"
                   ^^^

Some properties are allowed to be set in many types, but can only be get in one type, which is more convenient.

ref #2521

@otomad otomad added the Duplicate An existing issue was already created label Oct 19, 2022
@MartinJohns
Copy link
Contributor

MartinJohns commented Oct 19, 2022

I believe this is working as intended. Object literals don't use the setter method, so whatever logic may cause the value to transform is not running.

When you try to get this property the type will still wrongly be number.

The assignment works using at type assertion (as Foo), because that way you instruct the compiler to ignore most type errors, for example the missing property (which is then undefined, not Date).

@fatcerberus
Copy link

fatcerberus commented Oct 19, 2022

Also, even your first example (that you claim works) is wrong:

const foo = {} as Foo;
const bar = foo.bar; // typeof bar is Date
foo.bar = 123; // can be set by number
console.log(foo.bar);  // prints a number, not a date!

The only correct way to implement the interface Foo is with an actual getter/setter pair.

@MartinJohns
Copy link
Contributor

I'd also like to point out an unsoundness regarding such getter/setter: Even when you assign a valid object literal, you may end up with unsound behavior when assigning a value to the property.

interface Foo {
    get bar(): Date;
    set bar(value: Date | number);
}

const f: Foo = { bar: new Date() }
f.bar = 1;
console.log(typeof f.bar) // number

@otomad
Copy link
Author

otomad commented Oct 19, 2022

prints a number, not a date!

I just use interface Foo as an example. I'm just writing *.d.ts for a JavaScript library and some properties works like this.

@MartinJohns
Copy link
Contributor

and some properties works like this.

It works if the property is implemented accordingly (getter/setter). Your object literal doesn't do this.

@otomad
Copy link
Author

otomad commented Oct 20, 2022

I understand. That library is using a function that copies attributes of the literal object to another object through Object.assign (but that platform is lower, in fact, by copying them one by one with for in). So it actually just need to get all the setter types of the interface. However, it seems cannot get the type of setter.

@otomad otomad closed this as completed Oct 20, 2022
@fatcerberus
Copy link

@otomad See #43729.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants