-
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
Suggestion: a way to disable type widening in object literals #20195
Comments
We have talked in the past about a const o = { readonly x: 3 }; this allows the compiler to understand the intent of this object literal property, and not widen. |
works today: const o = new class { readonly x = 3 } |
I do not think the flag is right thing to do. such behaviors should not controlled by flags. The flags we have that alter the behavior are meant to be transitional, in other words, we would like everyone to turn them on, but acknowledge the need for a migration path. In this case, i am not convinced that |
@mhegazy that's still less than ideal, because in the limit it requires writing |
If const o = readonly { x: 3, y: 'hello' };
// o: { readonly x: 3; readonly y: 'hello' } Or if it were possible to write a function const o = makeReadonly({ x: 3, y: 'hello' });
// o: { readonly x: 3; readonly y: 'hello' } |
@pelotom you should make it clear of what exactly you expect when you say reading/writing is orthogonal to widening/narrowing it's by accident that |
@Aleksey-Bykov I agree, my concern here is with disabling type widening, and |
also consider this
|
because
is arguably more typing than
|
Also I should add that I personally don't mind conflating const o = readonly { x: 3, y: 'hello' }; would be attractive because it kills 2 birds with one stone. I currently don't use the |
be aware there was a discussion on literal literals (no puns): #10195 |
@Aleksey-Bykov I appreciate all of your proposed work-arounds; believe me when I say that I'm already making heavy use of such things, and find them unsatisfactory. This issue is for proposing an augmentation of the language which would make such workarounds unnecessary. |
i hear you, first class support would be ideal, i am convinced that literal type literals is the way forward |
I edited the main suggestion to be a proposal about |
Bump. This is still a greatly needed feature. Does the TypeScript have this problem on their radar? |
Always here, always watching |
@RyanCavanaugh I'm curious why this doesn't work: function readonly<O extends Readonly<Record<string, any>>>(o: O) {
return o
}
const result = readonly({ x: 3 })
// Expected type: { readonly x: 3 }
// Actual type: { x: number } |
he might be referring to this: #10195 (comment) |
@pelotom There's a similar issue with people using tuples for which they want the literal type inferred. By default TypeScript assumes you'll want to mutate it later, so it infers a widened type. I was looking for something like |
Unfortunately the game is already over by the time |
Readonly should be the default 👌 😩 const n = { x: 3 }
n.x++ // BOOM
const o = mut {x: 3, y: true}
o.x++ // Ugh, fine
👌 😫 |
I would agree if the language were being designed from scratch today, but barring a massive backwards-breaking change I think we are stuck now with |
@pelotom We have to keep moving forward. |
Ah, I found this issue right after creating a new one on #26979. There I propose a different approach to this, without needing much of new syntax. I don't know whether it's better or not, but be sure to check it out! 😉 Your proposal in a nutshell: const o = {
readonly a: 7;
}
o.a = 8; // readonly and wrong type My proposal in a nutshell: const o = {
a: 7 as const
}
o.a = 8; // wrong type! |
@qm3ster No, I proposed an alternative solution to the problem of automatic type widening in object literals. That has nothing to do with default immutability of everything. |
To be clear, this proposal is also (and primarily, I would say), about marking all of an object literal’s properties const o = readonly {
a: 7,
b: true
}
o.a = 8; // readonly and wrong type
o.b = false; // readonly and wrong type |
What about a compiler option to make all declared objects readonly by default (with a type flag |
@tycho01 well, that's not what I meant. Currently objects properties are mutable by default and we have |
We understand your proposal. The comment still applies. The proposal is neither a temporary patch for a breaking change in behavior (since mutable-by-default is the prefered way to go), nor a “stricter behaviors that we think users should move to”. That means it's a no-go for the maintainers of TypeScript. |
Preferred by whom? The absolutely major part of code in React projects forbids mutating anything. Angular also doesn't do well with mutability (since it checks only object references changes now). So why do you think it's not stricter behaviours that users should move to? |
heck, any static typing system doesn't do well with mutation.
for the record, I do, but they're stuck with all types of JS users ("any valid JS is valid TS!") and don't want their language to bifurcate. let's hope Wasm will save us from this. |
This continues to be a huge stumbling block for people trying to learn the language: https://twitter.com/kentcdodds/status/1081333326290415618 (As well as an annoyance for those who understand what’s going on!) |
I've been googling for the right issue for a while. Hopefully this is it. A variation of the main issue is when the value is provided directly. For example:
Ignoring the question about the return of the function The main characteristic of this example is that the string is defined in-place where it's passed directly to the function. There are workarounds, like This is inspired by this tweet which is about Redux Starter Kit. |
Definitely a common problem. As a workaround, I sometimes use // inferred {type: 'potatoes'}
const foo = {type: 'potatoes' as 'potatoes'};
// inferred {type: string}
const bar = {type: 'potatoes'}; Works with e.g. |
Also a weird case: const a = 0 // a: 0
const b = {readonly a} // b: {readonly a: number} 😕 |
Everyone continuing to comment on this issue should check out #29510. |
I understand the use of type widening with
let
andvar
variables (I useconst
ubiquitously so this is never a problem). But with object literals I pretty much never want the fields to be widened, and yet that's the default behavior:The only way I know of to work around this is with casts or type annotations, or by passing the object literal immediately to a function expecting the narrower type, thereby guiding type inference (although if the literal is being returned from a function even that technique won't work).
This is an eternal headache and it crops up in so many places. Would it be possible to add a compiler flag that makes it so that object literals don't get widened? I'm happy to be responsible for adding type annotations in the extremely rare cases where I actually want the widened type.
Edit
It looks like compiler flags are generally frowned upon to solve this problem, so let me amend my suggestion to take account of the discussion below. @mhegazy says,
This would certainly be handy, but on its own it's still not ideal because one ends up writing
readonly
in a lot of places. Take for example CSS-in-JS objects, which involve many properties whose types are unions of string literals. Every single one of those properties needs to then be markedreadonly
in order to defeat the scourge of type widening. But if one could declare an entire object literal to have only read-only properties like so:now this starts to look like a workable solution! So, let this be my amended suggestion:
readonly
modifiers for both object literal properties as well as object literals themselves, which has the side effect of disabling type widening.To the argument that
readonly
and type-widening are separate concerns and should be treated independently, to some extent I agree, but I also feel they are related; this is why thelet
vs.const
rules for type widening exist, and I think that rationale makes sense. I also personally don't mind conflatingreadonly
and type narrowing, because I prefer immutable objects everywhere too, so a syntax likewould be attractive because it kills 2 birds with one stone. I currently don't use the
readonly
keyword much (even though in spirit I want everything to be read-only) because the cost-benefit ratio of annotating every single property in every single object literal is too high.The text was updated successfully, but these errors were encountered: