-
Notifications
You must be signed in to change notification settings - Fork 2.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
types(remix-server-runtime): make AppLoadContext
an empty interface instead of any
#1876
Conversation
Hi @DanielFGray, Welcome, and thank you for contributing to Remix! Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once. You may review the CLA and sign it by adding your name to contributors.yml. Once the CLA is signed, the If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at [email protected]. Thanks! - The Remix team |
What does this do if you don't add that |
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
do you mean the
forgive me, I'm not sure I understand the question. currently it's impossible to override the type for |
If you don't add the |
This is how I currently override the type for export interface AppLoadContext {}
/**
* The arguments passed to ActionFunction and LoaderFunction.
*/
export type DataFunctionArgs<T extends (...args: any) => any> = Omit<Parameters<T>[0], 'context'> & {
context: AppLoadContext
}
/**
* A function that handles data mutations for a route.
*/
export interface ActionFunction {
(args: DataFunctionArgs<RemixActionFunction>): ReturnType<RemixActionFunction>
} |
Sorry, I'm not as well-versed in TypeScript as you might expect, I just know enough to get by. I understand this PR enables you to do this: declare module "remix" {
interface AppLoadContext {
graphql: <Result, Variables>(operation: {
query: TypedDocumentNode<Result, Variables>
variables: Variables
}) => Promise<ExecutionResult<Result>>
}
} What if in my app I don't declare anything and I add load context and try to access it, what will the type be? export let loader: LoaderFunction = async ({ context }) => {
// what is context? Any? Can I access stuff on it w/o typescript getting mad?
console.log("is TS mad?", context.foo);
} I'd like TypeScript to treat it as any. Another way to ask my question: does this PR now require an app to declare the |
That should be |
I don't know why this question is so hard for me to ask and get an answer that makes sense to me 😅 With this PR, do I get red squiggly lines under |
I think the answer is yes. Typescript playground example |
Do you have to have an empty interface to be able to do this or can you do it if it's typed as any? declare module "remix" {
interface AppLoadContext {
/* type here */
}
} |
There are other things like namespaces that support declaration merging, but |
You need to do that only if you want to add new properties to an interface. After that Typescript will merge the properties after you declare. |
You cannot do what this PR wants if it's typed as any. Updating a library's types via However, updating the PR with this would be close enough: export interface AppLoadContext {
[key: string]: any;
}; This would let you do Slight downside: |
This PR is just for better DX since esbuild doesn't care what you declare in the type system 😂 |
@itsMapleLeaf ah, okay, I can maybe live with that? Don't like that it has to be an object now, but seems weird to be anything else. @mjackson you have an opinion here? |
The other way is leaving it be |
@ryanflorence Just came here after almost having opened a similar PR myself. This is the signature for
This will continue allowing accessing any properties on |
AppLoadContext
an empty interface instead of any
@DanielFGray Code/type changes go against |
I have updated this to use @MichaelDeBoey I think I rebased this properly |
Looks like it includes changes that weren't in this PR. Might want to reflog and or rebase again. Looking forwards to this being merged in as we have a similar use case. |
@DanielFGray Like @marwan38 said: the rebase included changes that doesn't belong in this PR. |
@MichaelDeBoey just out of curiosity, at what point could we consider this stale or abandoned and give someone else a chance to get it across the finish line? I think this is a pretty large impact item for our company since our apps will all rely on this context. Let me know, I'm definitely willing to help! |
🦋 Changeset detectedLatest commit: 33fd91b The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@@ -16,7 +16,7 @@ import { createServerHandoffString } from "./serverHandoff"; | |||
|
|||
export type RequestHandler = ( | |||
request: Request, | |||
loadContext?: AppLoadContext | |||
loadContext: AppLoadContext |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this remain optional?
loadContext: AppLoadContext | |
loadContext?: AppLoadContext |
@@ -18,7 +20,7 @@ export async function callRouteAction({ | |||
match, | |||
request, | |||
}: { | |||
loadContext: unknown; | |||
loadContext: AppLoadContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadContext: AppLoadContext; | |
loadContext?: AppLoadContext; |
@@ -65,7 +67,7 @@ export async function callRouteLoader({ | |||
}: { | |||
request: Request; | |||
match: RouteMatch<ServerRoute>; | |||
loadContext: unknown; | |||
loadContext: AppLoadContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadContext: AppLoadContext; | |
loadContext?: AppLoadContext; |
@@ -82,7 +82,7 @@ async function handleDataRequest({ | |||
serverMode, | |||
}: { | |||
handleDataRequest?: HandleDataRequestFunction; | |||
loadContext: unknown; | |||
loadContext: AppLoadContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadContext: AppLoadContext; | |
loadContext?: AppLoadContext; |
@@ -180,7 +180,7 @@ async function handleDocumentRequest({ | |||
serverMode, | |||
}: { | |||
build: ServerBuild; | |||
loadContext: unknown; | |||
loadContext: AppLoadContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadContext: AppLoadContext; | |
loadContext?: AppLoadContext; |
@@ -519,7 +519,7 @@ async function handleResourceRequest({ | |||
serverMode, | |||
}: { | |||
request: Request; | |||
loadContext: unknown; | |||
loadContext: AppLoadContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadContext: AppLoadContext; | |
loadContext?: AppLoadContext; |
@justinwaite If you're willing to actively working on this, you can always open a new PR, since @DanielFGray isn't responding for over a month now. |
@MichaelDeBoey I literally just pushed a new commit yesterday. I'm still working out some of the kinks since a lot of things changed since I first filed this, and I'm also having trouble running the test suite locally which I've been asking for help with in the Discord #contributions channel. I'm very interested in having this merged and actively working fixing on the remaining failing tests. |
@DanielFGray Oh sorry, I looked over that commit, I only saw @justinwaite's message en then the Changeset message 🙈 @justinwaite Let's continue with this PR instead of creating a new one. |
Out of curiosity, what do you need the tests for? Running a type check should be all you need since all that's being change is the type definitions. EDIT: I just saw the pipelines, yes. You should be able to do |
Yeah sorry about the confusion – after making that post I reached out to him on Discord, and the rest is history 😄 Happy to help in whatever way! |
Hey all! Coming to this conversation a little late, but I've read through the approach here and it looks good 💯 We're putting some extra effort into type safety recently and this is the next thing on the docket. @DanielFGray let me know if there's any way I can help or support the awesome work you're doing! Happy to collaborate or support in whatever way. |
parameter used to be implicitly optional as it used to be typed as `any`. it _is_ intended to be optional, so this restores it as an optional param
… instead of `any` (#1876) * make AppLoadContext an empty interface * Update contributors.yml * AppLoadContext interface uses unknown for missing values * update types for AppLoadContext * fix: make `AppLoadContext` parameter optional parameter used to be implicitly optional as it used to be typed as `any`. it _is_ intended to be optional, so this restores it as an optional param Co-authored-by: Pedro Cattori <[email protected]>
If anyone is visiting this issue after banging your head against the wall, if you choose to keep the module augmentation in a separate file, you also need to import import "@remix-run/server-runtime";
declare module "@remix-run/server-runtime" {
interface AppLoadContext {
key: "value"
}
} (You may also replace import type { LoaderFunction } from "@remix-run/cloudflare" And everything is typed correctly. (Well, this change also makes |
I'm trying to address the optional issue here: #3989 |
This undos the exact version change pushed in JosteinKringlen#282 - remix-auth is moved to `peerDependencies` as the documentation for remix-auth-spotify specifies that it should be manually installed. - remix-auth is added to `devDependencies` for any tests that rely on remix have access to them. - remix-auth-oauth2 is made into a package range that will support all 1.x.x releases >= 1.5.0. This change was made because installing as specifed in the documentation lead to multiple versions of remix being installed which does not work with any typescript type overloading that you might do. remix-run/remix#1876 For example, I have an existing remix app using [email protected] with the following in my remix.env.d.ts: ```typescript // in remix.env.d.ts import '@remix-run/server-runtime' declare module '@remix-run/server-runtime' { interface AppLoadContext { requestId: string } } ``` This allows me to access the `requestId` in a `loader` like so: ``` import { LoaderArgs } from '@remix-run/node' export async function loader({ context }) { foobar(context.requestId) } ``` Installing remix-auth-spotify as specified in the docs caused [email protected] to be installed alongside 1.7.4. This caused the type overriding in Typescript to not work at all and make `context.requestId` become unknown. This was resolved by bumping up remix to 1.9.0 in my package.json. By using package ranges, downstream consumers of this package are able to define the versions of remix. Question: If accepted, would it make sense to also have remix-auth-oauth2 moved to `peerDependencies`? I could envision an authentication a situation where the upstream consumer of this package has Spotify auth AND a custom OAuth2 setup. I don't have a super strong stance, though.
This undos the exact version change pushed in JosteinKringlen#282 - remix-auth is moved to `peerDependencies` as the documentation for remix-auth-spotify specifies that it should be manually installed. - remix-auth is added to `devDependencies` for any tests that rely on remix have access to them. - remix-auth-oauth2 is made into a package range that will support all 1.x.x releases >= 1.5.0. This change was made because installing as specifed in the documentation lead to multiple versions of remix being installed which does not work with any typescript type overloading that you might do. remix-run/remix#1876 For example, I have an existing remix app using [email protected] with the following in my remix.env.d.ts: ```typescript // in remix.env.d.ts import '@remix-run/server-runtime' declare module '@remix-run/server-runtime' { interface AppLoadContext { requestId: string } } ``` This allows me to access the `requestId` in a `loader` like so: ```typescript import { LoaderArgs } from '@remix-run/node' export async function loader({ context }) { foobar(context.requestId) } ``` Installing remix-auth-spotify as specified in the docs caused [email protected] to be installed alongside 1.7.4. This caused the type overriding in Typescript to not work at all and make `context.requestId` become unknown. This was resolved by bumping up remix to 1.9.0 in my package.json. By using package ranges, downstream consumers of this package are able to define the versions of remix. Question: If accepted, would it make sense to also have remix-auth-oauth2 moved to `peerDependencies`? I could envision an authentication a situation where the upstream consumer of this package has Spotify auth AND a custom OAuth2 setup. I don't have a super strong stance, though.
This undos the exact version change pushed in JosteinKringlen#282 - remix-auth is moved to `peerDependencies` as the documentation for remix-auth-spotify specifies that it should be manually installed. - remix-auth is added to `devDependencies` for any tests that rely on remix have access to them. - remix-auth-oauth2 is made into a package range that will support all 1.x.x releases >= 1.5.0. This change was made because installing as specifed in the documentation lead to multiple versions of remix being installed which does not work with any typescript type overloading that you might do. remix-run/remix#1876 For example, I have an existing remix app using [email protected] with the following in my remix.env.d.ts: ```typescript // in remix.env.d.ts import '@remix-run/server-runtime' declare module '@remix-run/server-runtime' { interface AppLoadContext { requestId: string } } ``` This allows me to access the `requestId` in a `loader` like so: ```typescript import { LoaderArgs } from '@remix-run/node' export async function loader({ context }) { foobar(context.requestId) } ``` Installing remix-auth-spotify as specified in the docs caused [email protected] to be installed alongside 1.7.4. This caused the type overriding in Typescript to not work at all and make `context.requestId` become unknown. This was resolved by bumping up remix to 1.9.0 in my package.json. By using package ranges, downstream consumers of this package are able to define the versions of remix. Question: If accepted, would it make sense to also have remix-auth-oauth2 moved to `peerDependencies`? I could envision an authentication a situation where the upstream consumer of this package has Spotify auth AND a custom OAuth2 setup. I don't have a super strong stance, though.
this allows users to override the
context
with their own definitions using ambient modules.as an example: