-
-
Notifications
You must be signed in to change notification settings - Fork 257
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
readonly
considered as a breaking change?
#327
Comments
Thanks for reporting. Let's see how this is troublesome for others. |
Good idea! I'll stay on |
This also broke my build. I also agree that it is a useful change even though it is painful in the short term to change types across my whole code base. I do foresee this being troublesome for a lot of users. Would it make sense to add a small info section about this in the README? |
Thanks for the feedback. I'm not sure if the README is the right place. I added a note in the release note. Can you share some small code snippet that breaks with |
I found that any case where the type of a key is an array of something, i.e. |
Yeah, sorry, I know how it's causing the error. My question out of curiosity is how/where you specify |
Let me know if this helps: import { proxy } from 'valtio'
interface ProxyType {
someKey: readonly string[]
}
const testProxy = proxy({ someKey: [] }) as ProxyType
function doAThing (param: string[]): void {
console.log(param)
}
// The call below throws this:
// Argument of type 'readonly string[]' is not assignable to parameter of type 'string[]'.
// The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.
doAThing(testProxy.someKey) |
Thanks. ProxyType shouldn't be readonly. import { proxy, useSnapshot } from 'valtio'
interface ProxyType {
someKey: string[]
}
const testProxy = proxy<ProxyType>({ someKey: [] })
function doAThing (param: string[]): void {
console.log(param)
}
// in component
const snap = useSnapshot(testProxy)
doAThing(snap.someKey) // snap.someKey is `readonly string[]` |
What about using a wrapper hook for gradual migration? import { useSnapshot as useSnapshotOrig } from 'valtio'
type DeepWritable<T> = {
-readonly [P in keyof T]: DeepWritable<T[P]>
}
const useSnapshot = <T extends object>(proxyObject: T) => {
const snap = useSnapshotOrig(proxyObject)
return snap as DeepWritable<T>
} |
https://codesandbox.io/s/github/mysterybear/valtio-play @dai-shi should this be throwing a type error? I've wrapped the |
Ah, someone on TS Discord helped me out type HouseViewProps = {
house: DeepResolveType<HouseT>
} Maybe we could use some docs on this |
Thanks for reporting. I notice that this is a larger issue than I expected. It's not just about So, we didn't think const { houses } = useSnapshot(store) as HouseT For most users, DeepResolveType didn't make much sense because it was only useful when we use Suspense features. I would like to fix this problem (both of them) somehow. It may require a help from TS experts. There's one thing I want to solve: https://twitter.com/dai_shi/status/1480523055105724419 |
@dai-shi someone on TS Discord says
https://discord.com/channels/508357248330760243/885582487999365200/933899136850341979 |
That's good to know. Then, I will give up using |
I don't mind the |
Thanks for the feedback.
(I feel like I shouldn't have merged that PR, if I knew this.)
but, that's what I'm suggesting if you really need it. I really want to hide |
I stumbled upon a minor issue with readonly:
Is there any way to not add them and keep original types? |
Hi, my suggestion in your case is this (or that): const snap = useSnapshot(state) as typeof state Does it work? Maybe we should add this in readme. |
Does the trick, but feels a bit like a "hack around". Would it be viable to instead introduce something like I think there will be a lot of use cases where user's don't need to mutate these values, yet they have to be passed on to an api or something else that doesn't expect that readonly addition. |
I prefer to make it the other way around:
I don't disagree with this. It's something I really overlooked when reviewing the PR. But, there are opposite opinions from users. So, at least from the library perspective, it should use correct typing. |
That makes sense, perhaps |
currently I'm leaning to a recipe in readme rather than an exported function in utils, because there could be variations for various use cases. Let's see what others think. |
We have a project where we are using a generated API types where I just wanted to share a Typescript snippet that we used to ease the process... declare type AnyFunction = (...args: unknown[]) => unknown
export type DeepReadonly<T> = T extends (infer R)[]
? DeepReadonlyArray<R>
: T extends AnyFunction
? T
: T extends object
? DeepReadonlyObject<T>
: T
export type DeepReadonlyArray<T> = ReadonlyArray<DeepReadonly<T>>
export type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>
} @dai-shi would it maybe be helpful to export a similar utility to ease the process of migration? |
You don't need such a complicated type util. I hope eventually TypeScript supports it: microsoft/TypeScript#13923 Overall, I think people wouldn't necessarily want to revert |
Folks, I've got a better solution! With #359, we can do Module Augmentation. https://codesandbox.io/s/react-typescript-forked-qphpi?file=/src/App.tsx:46-125 declare module "valtio" {
function useSnapshot<T extends object>(p: T): T;
} |
fyi for others who found this thread, you can't add this module augmentation in |
We have a PR #458 open to document it. |
#458 is closed but the discussion there might be valuable. |
Stumbled on this as well earlier this week. I know the discussion has long been over but let me contribute anyway
Here's one such case: Typescript Playground Link In my case method consuming the value was defined in another module and it uses auto-generated type annotations of server responses from yet another module, which don't have readonly keywords anywhere in them. I like the idea of having readonly properties on objects returned from function useSnapshot<T extends object>(p: T): T; as was suggested above I went for a different approach and just undid readonly flags on arrays. But in order to do this I had to rely on the "internal" export // somewhere in my lib-env.d.ts
import 'valtio'; // probably unnecessary because of the next line
import { INTERNAL_Snapshot } from 'valtio';
type AnyFunction = (...args: any[]) => any;
type AnyArray = readonly unknown[];
type MutableArrays<T> = T extends AnyFunction ? T :
T extends AnyArray
? { -readonly [K in keyof T]: MutableArrays<T[K]> }
: { [K in keyof T]: MutableArrays<T[K]> }; // prettier-ignore
declare module 'valtio' {
function useSnapshot<T extends object>(p: T): MutableArrays<INTERNAL_Snapshot<T>>;
} Personally I would have prefer that to be default behaviour. It defeats the purpose, but if this is something that everybody has to dance around, maybe it is worth making the default |
To me, it looks like exactly the case we want to warn.
Yeah, that's problem, but it's only the user who know it. So,
In the next version, we don't unwrap promises, so the type should be simpler. We may revisit about considering exporting the snapshot type.
fwiw, the change is requested from a user, and it's correct typing and allows to catch errors at compile time. At this point, there's no reason to go back. |
To use the same deep-readonly method as the library itself (without re-implementing it as suggested above), I just did this and it seems to work as expected: type ReadonlyCat = ReturnType<typeof useSnapshot<Cat>>; |
I didn't know it was possible. Seems like it's already supported in TS4.7. Thanks for sharing! |
And this: https://tsplay.dev/WPJZZN |
Thank you, this was causing me problems as well. I was putting it into |
|
A
readonly
has been introduced in the recent minor 1.2.9 releasea983b8b#diff-b68a2cbc8606fe37f9c2c72867a87ff50a11c2b38a6d9de45a94281f02bf0a4eR252
Is this considered to be a breaking change? The change makes sense to me and it looks useful, but it in my case, I haven't explicitly marked props as
readonly
. My build breaks in ts strict mode and in order to fix it, I have to mark all props asreadonly
(I haven thought much about it before, but to me props are always immutable) where I pass data fromuseSnapshot
.The text was updated successfully, but these errors were encountered: