Readonly array in message options interface

Hey @Almeida I just saw your PR https://github.com/discordjs/discord.js/pull/10045 which broke my code. I'm unsure as to what the reason behind this is since an interface like BaseMessageOptions (the one inpacting me in particular) is meant to be the data sent to Discord, so it should be able to be modified. I'm trying to push components to a components array in a variable of type InteractionUpdateOptions, how should that be done after this change?
No description
62 Replies
d.js toolkit
d.js toolkit6mo ago
- What's your exact discord.js npm list discord.js and node node -v version? - Not a discord.js issue? Check out #other-js-ts. - Consider reading #how-to-get-help to improve your question! - Explain what exactly your issue is. - Post the full error stack trace, not just the top part! - Show your code! - Issue solved? Press the button! - Marked as resolved by staff
Rodry
RodryOP6mo ago
Also please tag me when you respond!
Syjalo
Syjalo6mo ago
I think this was made to make it possible to pass readonly arrays to the options
Rodry
RodryOP6mo ago
but this change as it sits restricts those arrays to always being readonly
Almeida
Almeida6mo ago
const components: ActionRow[] = [];

const responseData: ... = {
components,
...
}
const components: ActionRow[] = [];

const responseData: ... = {
components,
...
}
Rodry
RodryOP6mo ago
why would I do that? What do you gain from this? doing that just means having to store another global variable for no reason well not global, but high scope
Almeida
Almeida6mo ago
as syjalo said, that change allows passing readonly arrays whenever discord.js does not require a mutable reference (so pretty much everywhere) a mutable array can also be passed, as evidenced by my code sample
Rodry
RodryOP6mo ago
but this doesn't simply allow that, it also makes it impossible to pass mutable arrays but having to store another variable essentially creates two sources of truth for the same thing which is not good practice or rather, mutating the array after creating the variable, which I don't think should be a restriction
Almeida
Almeida6mo ago
it does not, given that components shares the same reference with responseData.components
Rodry
RodryOP6mo ago
but it means having to change the same thing twice changing the components on one variable and then the other options in another one it's just unnecessary extra work
Almeida
Almeida6mo ago
small price to pay for correctness imo
Rodry
RodryOP6mo ago
what correctess though? What error are you avoiding with this?
Almeida
Almeida6mo ago
already mentioned twice
Rodry
RodryOP6mo ago
you're adding support for a niche case while dropping support for one that seems much more common I agree that should be supported, but in a way that doesn't make it a breaking change and this was deployed in a minor version too and it's a heavily breaking change, it envolves changing code and not just typings unless I just decide to ignore this and force the type, which isn't good practice either
Syjalo
Syjalo6mo ago
Can you use satisfies?
const responseData = {
...
} satisfies BaseMessageOptions;
const responseData = {
...
} satisfies BaseMessageOptions;
Almeida
Almeida6mo ago
fairly certain changes to the types in discord.js do not follow semver
Rodry
RodryOP6mo ago
not really since this is a variable (not a constant) and can also be null it's a type change that directly affects the code and requires more than just type changes furthermore this code I'm showing is perfectly valid in JS, the error created by the type change doesn't exist in reality and in my opinion avoid it doesnt make the code any more or less correct because this is a variable I created and I know exactly the types of its properties
Almeida
Almeida6mo ago
feel free to adjust your types as you need to
Rodry
RodryOP6mo ago
but this is a change that was incorrectly done on the package side and wasn't even properly explained in the PR. I once again emphasize that the use case it's trying to fix is valid, but it shouldn't be done in a way that breaks existing, equally valid, setups I shouldn't need to be forcing the types of something that I know exactly what type it is I'm considering opening a PR to revert the changes to interfaces made by this PR, leaving the methods as they are since those shouldn't have this problem but interfaces from djs are often used by devs to type their own variables and not just used internally or as input types to methods
Syjalo
Syjalo6mo ago
For me this is not a breaking change, since BaseMessageOptions is what discord.js expects to get in options and the change just extended it allowing readonly arrays to be used. You can use your own type, TypeScript is not that strict in types.
Rodry
RodryOP6mo ago
it didn't extend, it forced the array to be readonly by adding the keyword or another option could be having a union type of array or readonly array, something like this
type ReadonlyOrMutableArray<T> = T[] | ReadonlyArray<T>
type ReadonlyOrMutableArray<T> = T[] | ReadonlyArray<T>
Almeida
Almeida6mo ago
this will not solve your issue
Syjalo
Syjalo6mo ago
What do you mean? I can use both
No description
Rodry
RodryOP6mo ago
that's once again not what I'm saying. I'm talking about a use case where you have the object typed as BaseMessageOptions in your code and you try to push to its components or embeds property. You cannot because that array is now readonly, even though it isn't. Despite it accepting both, when accessing it's always treated as readonly
Almeida
Almeida6mo ago
because accessing it would represent it being accessed by discord.js
Rodry
RodryOP6mo ago
but it shouldn't, I should be able to access that just fine in my code because this interface is exported if it was to be used by djs only it wouldn't be exported another option would be to have two interfaces, one with mutable arrays which is exported and the other one with imutable arrays which is only used in methods that accept that type the reason I want this to be fixed in the package itself is mostly because the type of this property is so complex that having to force the type of my variable to that would likely have issues in the future once it is updated and it would be difficult to track down @Almeida would you be able to do something like this in a PR?
Almeida
Almeida6mo ago
i think you underestimate the amount of interfaces in discord.js
Rodry
RodryOP6mo ago
the changes in your PR weren't that many
Almeida
Almeida6mo ago
i dont believe that is a good solution either way will only cause more confusion to the end user
Rodry
RodryOP6mo ago
then what is a good solution? I've only seen you throw down all the options I'm giving even though you caused the issue
Almeida
Almeida6mo ago
because i dont believe this to be a big issue (if at all). besides, this can easily be "worked around" on your end
Rodry
RodryOP6mo ago
how so? I'm not gonna be storing two variables that's ridiculous
Almeida
Almeida6mo ago
besides, i am not a maintainer, so feel free to make an issue or something so that maintainers can share their opinion
Rodry
RodryOP6mo ago
you're close enough there's barely a distinction between who is and who isn't here but we can also bring @vladdy who threw some eyes on another person that commented on that being a breaking change in that PR
DD
DD6mo ago
i don't think there's one person here that'll approve it lol i'm sorry man but your attitude just sucks, you're constantly so entitled it's hard to have a conversation but to get on topic the types are more correct as they are now and that's what's most important there were times we didn't follow that line of thought like
type Snowflake = `${bigint}
type Snowflake = `${bigint}
, not even particularly because it was inconvenient/not the best DX, but because it actually encouraged you to as cast so often it caused more harm than good in this case you get just a little cooked but it's more than acceptable by probably everyone's standards :meguFace:
Rodry
RodryOP6mo ago
I'm not entitled to anything, I'm simply exposing my situatio and trying to get you to understand the issue while also trying to find a solution, you're the ones who are so close-minded you only think about what's right for you and not for others the thing is that this change is barely benefitting anyone because most people don't go around declaring readonly arrays everywhere they go because it's just adding more clutter to the code and serving little to no purpose. I understand it might be "technically" correct but in my use-case it isn't because I need to be able to modify my code the way I want. I get that djs shouldn't modify the inputs people throw in but even that I have a hard time believing that it's actually followed since there are many transformations happening to inputs behind the scenes I genuinely understand what the goal of this change was, I just don't see any real benefits from it apart from theoretical correctness (and even that is debatable)
Danial
Danial6mo ago
there are many transformations happening to inputs behind the scenes
Can you please say where? And those inputs are typed as readonly?
Rodry
RodryOP6mo ago
just in the context of messages, the files are parsed and embeds and components are turned from builders to objects when applicable but that was not the main point of that message anyway
Danial
Danial6mo ago
Packages set a standard and people have to adapt to it, not the other way around, packages can't be changing things just because one person said so, if the package doesn't align with your goals, you shouldn't use it I don't see the original properties being modified anywhere, can you send what exactly you're talking about?
Rodry
RodryOP6mo ago
well it's not like there's much of an alternative is there once again that is not the main point I'm focusing on, I'm not trying to argue whether this change makes sense or not, I'm trying to draw attention to the issues it created and trying to find a solution
Danial
Danial6mo ago
You shouldn't claim stuff you can't back :shrug:
Rodry
RodryOP6mo ago
I am saying the principle behind the change makes sense, but it creates this issue which cannot be avoided in any way other than by forcing types, which is unsafe and you shouldn't be promoting that. Other than that, this change benefits close to no one because most people don't use the readonly keyword in a discord bot, just look at your average user here and you can see that very clearly My point is that, for many methods, what you pass to djs isn't what discord receives - just look at files for example - and that's fine, but you're completely replacing what the user sent with a parsed version, that doesn't seem very readonly to me
Danial
Danial6mo ago
Well if discord.js does nothing, what's the point of using it? read-only means that the parameter you pass in can't and won't be modified, it can clone it and make changes to that clone just fine, no issues there, and that's what discord.js does
DD
DD6mo ago
where is this done while mutating the original objects
Rodry
RodryOP6mo ago
Can we stay on the topic?
DD
DD6mo ago
this is literally the topic you made a point that the types aren't even right because we mutate internally
Rodry
RodryOP6mo ago
no I didn't. I've literally been saying it makes sense, it just doesn't add anything valuable to the DX because we don't use readonly stuff often in a discord bot and the average user in this server doesn't know what that is. I mentioned it may not be 100% correct due to some edge cases where the arrays are modified but I don't have the data or the exact lines to show you nor do I give a shit, so let's just talk about what matters shall we
DD
DD6mo ago
yes, people don't often explicitly annotate with readonly because it's typically optional, but saying "we don't use readonly stuff often in a discord bot" and "the average user in this server doesn't know what that is" is completely made up its projection, probably, not something you'd know so confidently to assert either way
Rodry
RodryOP6mo ago
you know it's true though, just open #djs-help-v14 for 5 minutes
DD
DD6mo ago
that doesn't tell me anything, looking at just that channel as a tell is inherently based surely you're old enough to understand that or intelligent enough, age shouldn't have much to do with it no one's gonna come to support and go "I got this and I agree with it and I know what it means and I fixed it myself", that wouldn't even be productive. you're only gonna see the confused & angry people there, which is really safe to assume are a minority moving on, if the primary means of designing APIs was around some alleged "skill level" or level of knowledge of the library's userbase we might as well just not do anything "average joe doesn't know what a readonly is" isn't a good reason to drop correctness that's a ridiculous mentality don't use typescript or tone down the strictness if you don't want to deal with that sort of thing its that easy not to mention more and more people use as const and satisfies and other TS features that have come out in recent years that help infer the readonly fields for you anyway
Rodry
RodryOP6mo ago
I would just like to know what you're actually gaining from this change because I just wrote this on 14.14.1 and it worked with no issues
No description
Rodry
RodryOP6mo ago
I think this is all just a bunch of theoretical correctness that doesn't translate to practicality or any actual real benefit in the real world feel free to prove me otherwise None of this was even mentioned in the PR, it just said "we're adding this rule because yeah" and went on with it, no before and after or even a motivation behind the change when you don't do extensive testing when changing types in this manner it makes sense that it has unwanted side-effects
Almeida
Almeida6mo ago
it does, as expected, not work in version 14.14.1
Almeida
Almeida6mo ago
No description
Rodry
RodryOP6mo ago
that's probably a tsconfig rule then because it literally does not error for me as you can see right above
Almeida
Almeida6mo ago
im afraid not its more likely that you were not using version 14.14.1 in your screenshot
Rodry
RodryOP6mo ago
I am I'm sure of that idk what else you want me to show you, it literally does not error
DD
DD6mo ago
yeah no this is you doing smth wrong everyone else can repro almeida's
Danial
Danial6mo ago
Yep, even without a tsconfig
Rodry
RodryOP6mo ago
I didn't even picked that version I just checked whatever my test bot had I do have a tsconfig I can even send it here if you really want
DD
DD6mo ago
ok man im convinced you just think everyone here is a moron or that your ego is just this big this isn't worth my time anymore, i'm out
Zach
Zach6mo ago
Yes, this PR broke my code base as well. It should not have been approved for a non-major release. I think the code author is likely just inexperienced and doesn’t fully understand the change they made. It’s a breaking change. I’m not attacking them, I’m explaining why I think a breaking change like that would be submitted and allowed into the code base. If I use discord.js’s typescript package in my application, I expect not to have the typing introduce breaking changes on non-major releases. Types aren’t added for convenience, they’re added to support compiler-enforced data invariants. The types are not very convenient when they break a code base. But fair enough, I will just lock the version in the future and expect other breaking changes in my typescript code base if I ever need to up version.
Want results from more Discord servers?
Add your server