Go To Definition to Fix Type Errors
Used in this guide:Introduction
This technique will help you resolve type errors in TypeScript, especially when working with third-party libraries. Instead of guessing or endlessly searching online, you'll learn how to investigate the exact types expected using Go To Definition in Visual Studio Code.
Scenario: Type errors in a Payload CMS user collection
Imagine you're working with a user collection in PayloadCMS with auth enabled. You have the following code in payload/collections/users.ts:
forgotPassword: {
generateEmailHTML: ({ req, token, user }) => {
const resetPasswordURL = `http://localhost:4011/reset-password?token=${token}`
return `
<div>
<h1>Reset your password</h1>
<p>Hi, ${user.email}</p>
<p>Click the link below to reset your password.</p>
<p>
<a href="${resetPasswordURL}">Reset Password</a>
<p>
</div>
`
}
}You may get type erros on the three arguments inside generateEmailHTML key such as:
Property 'req' does not exist on type '{ req?: PayloadRequest | undefined; token?: string | undefined; user?: any; } | undefined'.ts(2339)Property 'token' does not exist on type '{ req?: PayloadRequest | undefined; token?: string | undefined; user?: any; } | undefined'.ts(2339)Property 'user' does not exist on type '{ req?: PayloadRequest | undefined; token?: string | undefined; user?: any; } | undefined'.ts(2339)These errors mean that the argument you're destructuring may be undefined, or its properties are optional. Rather than fixing each error manually, there's a better approach:
Use Go To Definition in VSCode
You can perform "Go To Definition" by holding down ctrl left right cick on the key. In our case, we will click on the key generateEmailHTML.
VS Code will take you to where generateEmailHTML is defined. There, you might see:
generateEmailHTML?: GenerateForgotPasswordEmailHTML;This reveals that the value must conform to the type GenerateForgotPasswordEmailHTML. Next, Ctrl+Click on GenerateForgotPasswordEmailHTML to reveal its full definition:
type GenerateForgotPasswordEmailHTML<TUser = any> = (args?: {
req?: PayloadRequest;
token?: string;
user?: TUser;
}) => Promise<string> | string;This tells us everything about GenerateForgotPasswordEmailHTML:
- The function takes a single optional args object
- Each property on args is optional
- The type of each property
- The return type
With this information, we have two slutions we can use.
1. Destructure an Optional Argument (with Interface and Default)
Use this when you prefer destructuring but want to avoid TypeScript errors by providing a default fallback:
interface ForgotPasswordEmailArgs {
req?: PayloadRequest
token?: string
user?: {
email: string
[key: string]: unknown
}
}
forgotPassword: {
generateEmailHTML: ({ token, user }: ForgotPasswordEmailArgs = {}) => {
const resetPasswordURL = `http://localhost:4011/reset-password?token=${token ?? ''}`
return `
<div>
<h1>Reset your password</h1>
<p>Hi, ${user?.email ?? ''}</p>
<p>Click the link below to reset your password.</p>
<p>
<a href="${resetPasswordURL}">Reset Password</a>
</p>
</div>
`
}
}2. Use Dot Notation with Optional Chaining
Use this if you prefer simplicity and want to avoid destructuring entirely:
forgotPassword: {
generateEmailHTML: (args) => {
const token = args?.token ?? ''
const userEmail = args?.user?.email ?? ''
const resetPasswordURL = `http://localhost:4011/reset-password?token=${token}`
return `
<div>
<h1>Reset your password</h1>
<p>Hi, ${userEmail}</p>
<p>Click the link below to reset your password.</p>
<p>
<a href="${resetPasswordURL}">Reset Password</a>
<p>
</div>
`
}
}TypeScript is able to infer the type of args automatically from the PayloadCMS definition for generateEmailHTML. So even though you're not explicitly typing args, PayloadCMS already typed generateEmailHTML for you, and TypeScript applies that type as the default.