Implementing generic functions to modify characteristics of a type in TypeScript

Currently, I am developing a Next.js application using TypeScript, and one of the types is as follows:

type GetStaticPropsContext<Q extends ParsedUrlQuery = ParsedUrlQuery, D extends PreviewData = PreviewData> = {
    params?: Q | undefined;
    preview?: boolean | undefined;
    previewData?: D | undefined;
    locale?: string | undefined;
    locales?: string[] | undefined;
    defaultLocale?: string | undefined;
}

To simplify my code and avoid constantly using as string and as string[] castings for locale, locales, and defaultLocale, I want to ensure that they are never undefined. In my application, these values are always defined.

I attempted to create a new type like this:

type LocaleConfig = {
  locale: string
  defaultLocale: string
  locales: string[]
}

Then, I tried using it in the following way:

export const getStaticProps: GetStaticProps = (
  context: GetStaticPropsContext & LocaleConfig
) { // do stuff }

However, this resulted in an error stating

Type 'string | undefined' is not assignable to type 'string'.
.

I also experimented with creating an interface that extends GetStaticPropsContext, but it seems like I would need to duplicate the generics, which is less than ideal:

export interface NewGetStaticPropsContext<
  Q extends ParsedUrlQuery = ParsedUrlQuery,
  D extends PreviewData = PreviewData
> extends GetStaticPropsContext {
  params?: Q | undefined
  previewData?: D | undefined
  locale: string
  defaultLocale: string
  locales: string[]
}

Answer №1

It is highly likely that the error message is caused by the GetStaticProps type:

Should it be structured like this:

type GetDynamicProps = (context: GetStaticPropsContext) => void;

...then the issue arises when attempting to assign a function with required arguments to the getStaticProps variable, as TypeScript does not permit assigning a stricter function to a more "permissive" one.

type GetStaticPropsContext<Q extends ParsedUrlQuery = ParsedUrlQuery, D extends PreviewData = PreviewData> = {
  params?: Q | undefined;
  preview?: boolean | undefined;
  previewData?: D | undefined;
  locale?: string | undefined;
  locales?: string[] | undefined;
  defaultLocale?: string | undefined;
}

type LocaleConfig = {
  locale: string
  defaultLocale: string
  locales: string[]
}

export const getStaticProps = (
  context: GetStaticPropsContext & LocaleConfig
) => { } // Okay

type GetStaticProps = (context: GetStaticPropsContext) => void

const getStaticProps2: GetStaticProps = getStaticProps;
// Type '(context: GetStaticPropsContext & LocaleConfig) => void' is not assignable to type 'GetStaticProps'.
// Type 'string | undefined' is not assignable to type 'string'.
// Type 'undefined' is not assignable to type 'string'.

Playground Link

When declaring getStaticProps2 as a function of type GetStaticProps, it implies that we can call it without any arguments since all properties in GetStaticPropsContext are optional:

getStaticProps2({});

However, if the getStaticProps function is assigned instead, certain properties become mandatory...


To transform a type so that some of its optional properties are made required, utilizing an intersection (&) is recommended.

You could even employ a combination of Required and Pick utility types to prevent errors and ensure matching types:

type LocaleConfig = Required<Pick<GetStaticPropsContext, "locale" | "defaultLocale" | "locales">>
//   ^? { locale: string; defaultLocale: string; locales: string []; }

export const getStaticProps = (
  context: GetStaticPropsContext & LocaleConfig
) => {
  // do stuff
  const locale = context.locale
  //    ^? string
  const preview = context.preview
  //    ^? boolean | undefined
}

getStaticProps({
  locale: "locale",
  defaultLocale: "",
  //locales: [] // Error if any of the Required properties is missing
});
// Property 'locales' is missing in type '{ locale: string; defaultLocale: string; }' but required in type 'Required<Pick<GetStaticPropsContext<ParsedUrlQuery, PreviewData>, "locale" | "defaultLocale" | "locales">>'.

Playground Link

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The computed value fails to identify the prop object

I am trying to create a computed value that I can use in my view by following this documentation: Within a component, I am sending an array object like this: const tabs = ref([ { imageSrc: '/programs/test1.png', .... ...

What is the best way to relocate the styles folder to the src folder while using nextjs, typescript, and tailwind

I am currently working with Next.js using TypeScript and Tailwind CSS. My goal is to relocate the styles folder into the src folder. I have already updated the baseUrl in my tsconfig.json file to point to the src directory, but I encountered the following ...

What is the easiest method for distributing one of my libraries across multiple React Typescript projects?

In my React projects, I often find myself needing to share common data object or utility classes locally. For instance, a utility class that handles detailed string or data structure manipulations. What would be the most effective approach for this? Shoul ...

What are the steps to effectively utilize a jest mock within a typed test fake when working with typescript?

I am facing an issue where I have defined an interface that requires certain functions to be implemented by objects. For instance: interface MyInterface { someFunc: () => void, /* ... other details */ } Now, I need to create a test to ensure th ...

Using Array.push within a promise chain can produce unexpected results

I have developed a method that is supposed to retrieve a list of devices connected to the network that the client has access to. export const connectedDevicesCore = (vpnId: string, vpnAuthToken: string) => Service.listVPNConnectionsCore ...

Cannot see the template on the Angular Typescript component

After encountering and resolving this issue: AngularJS directive not displaying the template I decided to experiment with an Angular component and TypeScript, but unfortunately, I can't seem to make it work. The component refuses to display. This is ...

Exploring new features in Angular 12: navigating and refreshing pages

I have an Angular application where I need to reload the page after navigation. I tried using this solution, but the window.location.reload(); method does not work in production. As a workaround, I added useHash: true in the app-routing.module.ts file. How ...

Exploring Angular 2/4: Unpacking the Process of Accessing API Data Using Tokens

Hello there, I am trying to retrieve API data with a token using Angular 2/4. Below is the code I have written: import { Component, ViewEncapsulation } from '@angular/core'; import { Http, Response } from '@angular/http'; import &apos ...

Issue with importing CSS/SASS into Vue Cli 3 Typescript within the <script> block

Recently, I created a new Vue app using TypeScript with Vue Cli 3. However, when attempting to import CSS or Sass into my TypeScript file, I encountered the following issue: import * as sassStyles from '@/assets/sass/my.module.scss'; This re ...

Angular 4 Web Application with Node-Red for Sending HTTP GET Requests

I am creating a unique service that utilizes Node-red to send emails only when a GET request is made to 127.0.0.1:1880/hello (node-red port), and an Angular 4 web app (127.0.0.1:3000) for client access. Upon accessing the /hello page from a browser, I rec ...

Creating Mongoose models in TypeScript with multiple connections

Attempting to build a model with multiple connections as per the documentation, I encountered the following error: TS2345: Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of ty ...

The error message "Cannot find property 'X' on type 'number' in React TypeScript" is appearing

When iterating over the list of brands, I am facing an issue where the brand properties are not loading properly. During this time, the indexed array is displayed as a skeleton structure. While JavaScript handles this situation well, TypeScript encounter ...

Guide on utilizing the main development dependencies in Vue 3 Mono Repo

After setting up a mono repo using Ionic and Vue 3, I encountered an issue where I had to manually add devDependencies into the package.json file of each child app. Is there a way to utilize the root devDependencies instead? "devDependencies": { ...

Simple and quickest method for incorporating jQuery into Angular 2/4

Effective ways to combine jQuery and Angular? Simple steps for integrating jQuery in Angular2 TypeScript apps? Not sure if this approach is secure, but it can definitely be beneficial. Quite intriguing. ...

What is the best way to retrieve an object from a loop only once the data is fully prepared?

Hey, I'm just stepping into the world of async functions and I could use some help. My goal is to return an object called name_dates, but unfortunately when I check the console it's empty. Can you take a look at my code? Here's what I have ...

The specified type 'BallInterface' must have 2 type arguments specified

Currently on my Typescript learning journey, I encountered an error that states Generic type 'BallInterface' requires 2 type argument(s) in relation to tennisBall. How can I properly call a function with an object parameter containing multiple ge ...

Using Typescript to inject `require(...)` rather than importing files

I am currently in the process of compiling a third-party module called pdfassembler and I want to ensure that the source code for the import statements is included in the compiled output instead of references to require statements. Within the src/pdfassem ...

Learn how to dynamically disable unchecked checkboxes and apply specific CSS classes to checked checkboxes in Angular 12 with TypeScript

Currently, I am working on a project where I have successfully stored checkboxes dynamically using a JSON array of objects. However, I am facing an issue that requires your expertise. My goal is to allow the selection of only 5 checkboxes. Once this limit ...

Error 404 returned when making an Angular HTTP POST request to a Heroku application

I am currently encountering an issue with my Angular project, where I am trying to send a POST Request to an Express Server hosted on Heroku in order to send out an email. However, every time I send the POST request to the server, I receive a 404 error. M ...

Tips for successfully mocking axios.get in Jest and passing AxiosPromise type value

I attempted to simulate the axios.get() function using the code below, however TypeScript is returning an error stating "argument of type '{ data: expectedResult }' is not assignable to parameter of type 'AxiosPromise<{}>'". Can ...