-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
[Optional feature] Mutable references like in Rust with "mut" #56409
Comments
This isn't something we'd be keen to adopt. The level of infectiousness here is very high (same as |
I don't think you understand. This only affects one codebase and if you are using a library that doesn't use it, you can still use it, but if a library does you it, you don't have to. TypeScript as a whole is considered "infectious" in that sense, yet we still use it. I don't know what you mean by the ReadonlyArray bit, because everyone just writes |
I know about the idea of optional features, yes. That doesn't mean they appear for free, or have no other concerns, or don't represent ongoing cost to maintain and reason about as they interact with other new features. |
What do you even mean? I never said it was free. What did you mean about ReadonlyArray because it makes no sense |
How is this "too complex"? You just can't mutate something if it's not marked as |
As proposed, I think this is actually more dangerous than the status quo. Scenario:
|
@fatcerberus No, it would see you didn't use this feature and just mark everything as mut. |
@fatcerberus Since you didn't use the feature, that means it is like how it is now, which means it is mutable. You'll have to specify mut in loads of places possibly, but that's actually good considering it's the worst case scenario. |
I don't think you understand - compiler options are not per-file, they are per-project, and extend even to imported modules. TS would have absolutely no way of knowing that the library author didn't enable the option to enforce this, it would just see that the |
@fatcerberus And tsconfig.json is also per project and not per file so I'm not sure you understand. |
Yes, and your tsconfig applies even to the libraries you import. Which may mutate stuff without being marked |
@fatcerberus If that were true, so many things would be broken. It only applies your options to your project, including when you use other libraries, but not the internals of the library. If it only checked for mut blindly, it would be a problem, but you can just check that before. For example, if a library doesn't have explicit return types but you require explicit return types, it's not an issue, same with implicit overrides. |
Let me spell this out. Let's suppose you enabled /* myStuff.ts - in your project */
// written in the future, when strict mutability is available
import { mutateArray } from 'theirStuff'
const x: string[] = [ "foo", "bar" ]
// x.push("qux") // type error, `this` is not mutable
mutateArray(x) // not an error!
/* theirStuff.d.ts - buried somewhere in node_modules */
// generated by current TS, before the `mut` keyword existed
declare function mutateArray(x: string[]): void Since you have strict mutability enabled and all TS sees of |
@fatcerberus Yes the declaration is using the current way, but since we don't know, that means it's not enabled. Or we could have the opposite, a readonly keyword. In a project that uses mut, it would set anything that isn't explicitly readonly as such and then if nothing is specificied, that means it can't be using mut and is therefore mut. |
@RyanCavanaugh how would you solve this problem here? type X = { a: number }
type UnReadonly<T extends Record<string, any>> = {
-readonly [K in keyof T]: T[K]
}
declare const j: Readonly<X>
function i(e: UnReadonly<X>) {
e.a = 1
}
i(j) Clearly this shouldn't be allowed. It doesn't even work if when you manually specify it, because it doesn't see a difference between must being writable and not being readonly. |
Currently types with readonly properties are implicitly compatible with writable properties. The open issue for this is #13347. Currently there is no way to represent immutability in the type system. Read-only does not mean immutable. |
π Search Terms
mutable, mut, mutable references
β Viability Checklist
β Suggestion
It's very common to accidentally mutate an object and not be aware of it, or purposefully mutate something but it's hard to keep track of it. Rust solves this problem with the
mut
keyword, likelet mut x = something
orfn x(y: &mut Something) {}
. This would be an opt-in feature and the implementation may differ from the Rust one, but the general idea is that if you want to mutate a variable, you have to use themut
keyword and if you want to mutate a function parameter, both the function parameter and function argument when you call the function must use themut
keyword. If you want to mutatethis
in a function, you must also use the mut keyword usingthis
as a parameter (sincethis
is a reserved word, it already let's you do this, for example(this: Something) => void
.π Motivating Example
The
ReadonlyArray
type in TypeScript is quite flawed because it requires a separate type for arrays that are readonly. It relies on the fact that the devs made all readonly arrays be of theReadonlyArray
type and that they also knew if a function mutated the array or not. There is simply no way for TypeScript to know that an array with a fixed length shouldn't have the push method. How could we tell it? With themut
keyword specifying whether a method mutates itself. For example, the type definition of push would change from thispush(...items: T[]): number
to thispush(mut this, ...items: T[]): number
. Now TypeScript knows this only works on mutable arrays and not immutable ones. So this would workconst mut array = [1, 2, 3]; array.push(4)
, but this would failconst array = [1, 2, 3]; array.push(4)
, because the push method requiresthis
to be mutable, but it isn't.π» Use Cases
The text was updated successfully, but these errors were encountered: