-
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
Variant accessors #42425
Variant accessors #42425
Conversation
@typescript-bot pack this |
Heya @RyanCavanaugh, I've started to run the tarball bundle task on this PR at 6f09705. You can monitor the build here. |
Hey @RyanCavanaugh, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build and an npm module you can use via |
@typescript-bot pack this |
Heya @RyanCavanaugh, I've started to run the tarball bundle task on this PR at d2affa2. You can monitor the build here. |
Hey @RyanCavanaugh, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build and an npm module you can use via |
This probably also fixes #32821 |
What milestone will this go into? |
…ariant accessor types in TypeScript This make accessors have the correct types to represent all possible values that they can be set to, but the getters are funky because to use them it requires type casting. As a temporary workaround, `get*()` methods have been added (f.e. `element.getPosition()` returns the same as `element.position` but with the value casted to the underlying object type. This does not impact JavaScript users. They can continue to do things like `element.position.x = 123`. But TypeScript users can't do that. They would have to write `;(element.position as XYZNumberValues).x = 123` which is cumbersome, but the temporary getter methods allow TypeScript users to write `element.getPosition().x = 123` instead. TypeScript users can still set values as usual: `this.position = [1, 2, 3]` just like JavaScript users. The upcoming changes in microsoft/TypeScript#42425 will allow us to update the getter types so TypeScript users can write `element.position.x = 123`.
d2affa2
to
b7f93bb
Compare
@typescript-bot pack this |
Heya @RyanCavanaugh, I've started to run the tarball bundle task on this PR at b7f93bb. You can monitor the build here. |
Hey @RyanCavanaugh, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build and an npm module you can use via |
# Conflicts: # src/compiler/checker.ts # src/compiler/diagnosticMessages.json # tests/baselines/reference/privateNamesAndGenericClasses-2.errors.txt
@ahejlsberg thanks, addressed at #43405 |
This will depend on microsoft/TypeScript#42425 landing in stable TypeScript so we can update the types to reflect it.
@typescript-bot pack this |
What's the syntax (if supported) for generic types? type MarkAsString<T extends Record<string, any>> = {
[K in keyof T]: T extends number ? T[K] /* How to set add a string|number set here? */ : T[K]
}
declare const a: MarkAsString<{ a: boolean, b: number, c: string }>
a.b = '42'; // I want to have this avalible
a.c = '112'
// @ts-expect-error
a.a = '2' Actual use caseIn Vue3 there's const r = ref({ a: 1} )
r.value.a = 1
const a = reactive(r)
a.a // 1 On a const r = ref({a:1});
const a = ref({
r
}) // results in `{ r: { a: 1 } }`
a.r = r; // typescript error because `r` is `Ref<T>` instead of `UnwrappedRef<T>`, but it works at runtime. |
I think what you're actually looking for is how to use the new feature with index signatures. My first guess would have been something like
but that doesn't work on the latest Playground for this build. Maybe it's not currently possible? |
Yeah, that's a totally separate and additional feature. |
Cool, should I open an issue? |
Sure |
This is very cool, is there any information on which version will have this change? |
@V1raNi This has been shipped in 4.3 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html#separate-write-types-on-properties |
The different type for setter doesn't work for Union type as well. Is this expected? Example can be found here |
Oh, thank you very much. It's just I was interested in different visibility and couldn't find it in the text, and the docs seem to be outdated a bit regarding this part. |
Getter / Setter Variation
Implements #2845 and #2521
Overview
This implements two related features:
Differing Types
Property getters and setters may now differ in their types:
Restrictions
As a conservative implementation, a few restrictions are in place.
First, the type of the getter must be assignable to the type of the setter. In other words, the assignment
must be legal assuming
obj.x
is writable at all.This restriction closes off a certain set of use cases for properties that start out "uninitialized" (usually
null
/undefined
) but then only want "initalizing" assignments to occur. We'll continue to examine these to see if those use cases are prevelant enough to warrant opening this up more.These prevent novel unsoundness from occurring and makes this feature unimpactful from a type relational perspective, which limits its risk.
Differing Visibility
In classes,
set
accessors may now be less visible than their correspondingget
accessor. It looks like this:Type Relationship Effects
TL;DR: there are none
TypeScript is already covariant when relating properties of types. The unsoundness of this is straightforward to demonstrate during aliased writes:
TypeScript also already ignores the
readonly
modifier when relating types, so theprotected
orprivate
state of a setter does follows the same pattern.Caveats
TL;DR: The type system remains entirely covariant in other operations! This has some effects that may not be immediately apparent.
Types with variant getters/setters are effectively reduced to their
get
side when put through mapped types:This is fairly straightforward to reason about -- a mapped type usually represents a "copy with transform" operation, e.g.
In other words, for an arbitrary mapped type, we have no idea how its actual value is produced, and the only sound assumption is that this type doesn't copy over any coercing semantics from its setters.
The same applies to lookup types -- the type
T[K]
still means *the read type ofK
onT
. A freesetter
function can't be used to indirectly access the coercing side of the setter: