-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
feat(ts): support module augmentation #1681
Changes from all commits
e4558e8
f0fc4da
10b2987
8e604c6
06de62d
ad89db2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,10 +4,10 @@ | |
|
||
import { ConnectionOptions } from "typeorm" | ||
import { Adapter } from "./adapters" | ||
import { JWTEncodeParams, JWTDecodeParams, JWTOptions, JWT } from "./jwt" | ||
import { JWTOptions, JWT } from "./jwt" | ||
import { AppProvider, Providers } from "./providers" | ||
import { NextApiRequest, NextApiResponse, NextApiHandler } from "./_next" | ||
import { NonNullParams, WithAdditionalParams } from "./_utils" | ||
import { Awaitable, NonNullParams, WithAdditionalParams } from "./_utils" | ||
|
||
export interface NextAuthOptions { | ||
providers: Providers | ||
|
@@ -62,30 +62,34 @@ export interface AppOptions | |
providers: AppProvider[] | ||
} | ||
|
||
export interface CallbacksOptions { | ||
signIn?: | ||
| (() => true) | ||
| (( | ||
user: User, | ||
account: Record<string, unknown>, | ||
profile: Record<string, unknown> | ||
) => Promise<never | string | boolean>) | ||
redirect?: (url: string, baseUrl: string) => Promise<string> | ||
session?: | ||
| ((session: Session) => WithAdditionalParams<Session>) | ||
| (( | ||
session: Session, | ||
userOrToken: User | JWT | ||
) => Promise<WithAdditionalParams<Session>>) | ||
jwt?: | ||
| ((token: JWT) => WithAdditionalParams<JWT>) | ||
| (( | ||
token: JWT, | ||
user: User, | ||
account: Record<string, unknown>, | ||
profile: Record<string, unknown>, | ||
isNewUser: boolean | ||
) => Promise<WithAdditionalParams<JWT>>) | ||
export interface Account extends Record<string, unknown> { | ||
accessToken: string | ||
idToken?: string | ||
refreshToken?: string | ||
access_token: string | ||
expires_in?: number | null | ||
refresh_token?: string | ||
id_token?: string | ||
id: string | ||
provider: string | ||
type: string | ||
} | ||
export interface Profile extends Record<string, unknown> {} | ||
|
||
export interface CallbacksOptions< | ||
P extends Record<string, unknown> = Profile, | ||
A extends Record<string, unknown> = Account | ||
> { | ||
signIn?(user: User, account: A, profile: P): Awaitable<string | boolean> | ||
redirect?(url: string, baseUrl: string): Awaitable<string> | ||
session?(session: Session, userOrToken: JWT | User): Awaitable<Session> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This type will force the session?:(session: Session, userOrToken?: JWT | User): Awaitable<Session> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, so the defulat session callback actually is just an identity function and does nothing than return the first param. In any other case, when the user defines/overrides this, they will use the second param to extend session in some way. So I think it's good. 🙂 |
||
jwt?( | ||
token: JWT, | ||
user?: User, | ||
account?: A, | ||
profile?: P, | ||
isNewUser?: boolean | ||
): Awaitable<JWT> | ||
} | ||
|
||
export interface CookieOption { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,77 @@ | ||
--- | ||
id: typescript | ||
title: TypeScript Support | ||
title: TypeScript | ||
--- | ||
|
||
Currently, NextAuth.js relies on the community to provide TypeScript types. You can download it from [DefinitelyTyped](https://www.npmjs.com/package/@types/next-auth). | ||
NextAuth.js comes with its own types, so you can safely use it in your TypeScript projects. Even if you don't use TypeScript, IDEs like VSCode will pick this up, to provide you with a better developer experience. While you are typing, you will get suggestions of what certain objects are, and sometimes also links to documentation, and examples. | ||
|
||
Add it to your project with: | ||
:::note | ||
The types at [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) under the name of `@types/next-auth` are now deprecated, and not maintained anymore. | ||
::: | ||
|
||
```sh | ||
npm i -D @types/next-auth | ||
*** | ||
## Module Augmentaion | ||
|
||
`next-auth` comes with certain types/interfaces, that are shared across submodules. Good examples are `Session` and `JWT`. Ideally, you should only need to create these types at a single place, and TS should pick them up in every location where they are referenced. Luckily, this is exactly what Module Agumentation can do for us. Define your shared interfaces in a single location, and get type-safety across your application, when you use `next-auth` (or one of its submodules). | ||
|
||
1. Let's look at `Session`: | ||
|
||
```ts title="pages/api/[...nextauth].ts" | ||
import NextAuth from "next-auth" | ||
|
||
export default NextAuth({ | ||
callbacks: { | ||
session(session, token) { | ||
return session // The type here should match the one returned in `useSession()` | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
or | ||
```ts title="pages/index.ts" | ||
import { useSession } from "next-auth/client" | ||
|
||
```sh | ||
yarn add -D @types/next-auth | ||
export default function IndexPage() { | ||
// `session` should match `callbacks.session()` in `NextAuth()` | ||
const [session] = useSession() | ||
|
||
return ( | ||
// Your component | ||
) | ||
} | ||
``` | ||
|
||
You can find an initial Pull Request at [next-auth#516](https://github.com/nextauthjs/next-auth/pull/516) adding TypeScript. At the time of this writing, it looks like we would like to go from a complete migration to a more relaxed, incremental rewrite. | ||
To extend/augment this type, create a `types/next-auth.d.ts` file in your project: | ||
|
||
```ts title="types/next-auth.d.ts" | ||
import NextAuth from "next-auth" | ||
|
||
declare module "next-auth" { | ||
interface Session { | ||
user: { | ||
/** The user's postal address. */ | ||
address: string | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Make sure that the `types` folder is added to [`typeRoots`](https://www.typescriptlang.org/tsconfig/#typeRoots) in your project's `tsconfig.json` file. | ||
|
||
2. Check out `JWT` also: | ||
|
||
```ts title="types/next-auth.d.ts" | ||
declare module "next-auth/jwt" { | ||
interface JWT { | ||
/** OpenID ID Token */ | ||
idToken?: string | ||
} | ||
} | ||
``` | ||
|
||
Note that this time we declared `JWT` inside `next-auth/jwt`, as this is its default location. | ||
|
||
|
||
## Contributing | ||
|
||
Feel free to open a Pull Request, if you would like to contribute! | ||
Contributions of any kind are always welcome, especially for TypeScript. Please keep in mind that we are a small team working on this project in our free time. We will try our best to give support, but if you think you have a solution for a problem, please open a PR! |
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.
Given
Profile
is justRecord<string, unknown>
, maybe we can simplify this line to 💭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.
It is because it's kind of unfinished, as we may want to make it extensible as the other interfaces. We could either try to somehow infer it from providers, or add it as a generic to NextAuth (same goes for Account, they are not necessarily used anywhere else than NextAuth, so generic could work just fine. Or we could go all in on Module Augmentation. Haven't decided yet. 🤔) In any case, I think that could be a subsequent PR