Can we ensure that inline object definitions only reference existing fields?

I need to create an object where the fields are required to be of a specific type (CSSProperties in this particular case, but ultimately I want a generic type).

Using an indexed type (or Record) allows TypeScript to enforce the correct type for field values, leading to error messages if there are any misdefined contents. However, TypeScript loses track of explicitly defined fields, so referencing an undefined field will not trigger a compilation error and will result in returning undefined.

export const simpleStyle = {
  a: {
    margin: 0,
  },
  b: {
    margin: 0,
    wibble: 'blah', // no compile error: BAD
  },
} 

const simpleA = simpleStyle.a;
// compiles with an error message: GOOD
// const simpleB = simpleStyle.wibble;

export type CssStyles = { [index: string]: CSSProperties };

export const typedStyle: CssStyles = {
  a: {
    margin: 0,
  },
  b: {
    margin: 0,
    // triggers a compile error: GOOD
    // wibble: 'blah',
  },
}

const typedA = typedStyle.a;
// does not produce a compilation error
const typedB = typedStyle.wibble;

What is the method to define this type in such a way that TypeScript considers references to undefined indexes as incorrect? The determination of "defined" should rely on the object's declaration content (avoiding the need to redefine all fields in a union or specify an explicit type).

Answer №1

To ensure a specific type while inferring a more detailed subtype, utilizing a generic function is necessary.

function createStyles<K extends string>(
  styles: Record<K, CSSProperties>
): Record<K, CSSProperties> {
  return styles
}

In this scenario, the objective is to deduce the keys of the object while enforcing strict typing for the values. This involves capturing the keys as K and employing them as the keys for the object that the function receives.

This approach also incorporates the Record utility type to associate keys with values within an object.

The functionality aligns with expectations, as demonstrated below:

export const typedStyle = createStyles({
  a: {
    margin: 0,
  },
  b: {
    margin: 0,
    wibble: 'blah', // error
  },
})

const typedA = typedStyle.a;
const typedB = typedStyle.wibble; // error

Playground


It is worth noting that the Typescript team is currently developing a feature to potentially eliminate the need for this function using the satisfies operator.

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

Dramatist: Unable to import session storage data from a JSON file into the browser environment

My task is to test an application built with React and NestJS. The session token can be found in the sessionStorage of my browser. To guide me through this process, I referred to the relevant part of the Playwright documentation: https://playwright.dev/ ...

The input of type 'Observable<true | Promise<boolean>>' cannot be assigned to the output of type 'boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>'

I'm currently using a Guard with a canActivate method: canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.fi ...

What is the correct way to type "this" to ensure it works properly when called in a subclass method?

Let's consider including an extension method to the existing Object: declare global { interface Object { ext<B>(f: (x: this) => B): B; } } The aim is to apply it as shown below: "x".ext(x => x.toUpperCase()) //or (1).ext(x => ...

Invoke cloud functions independently of waiting for a response

Attempting a clever workaround with cloud functions, but struggling to pinpoint the problem. Currently utilizing now.sh for hosting serverless functions and aiming to invoke one function from another. Let's assume there are two functions defined, fet ...

Encountered an issue with Webpack 5 - A ReferenceError was thrown: require is not recognized

I encountered an error while attempting to access the main page of my app in the browser: Uncaught ReferenceError: require is not defined at Object.events (main.bundle.js:90508:1) at __webpack_require__ (main.bundle.js:91217:33) at fn (main.bundle.js:91451 ...

Leveraging the 'styled()' utility from the MUI System while incorporating extra properties (Typescript)

I'm currently tackling a project using MUI System v5. I've opted to use the styled() utility (not styled-components) for styling and creating basic UI components. TypeScript is being used in this project, but I am facing a number of challenges as ...

Running the NPM build command results in an error specifically related to an HTML file

I encountered an issue in my AngularJS application when running the command: npm run build -- -prod The error message I received was: ERROR in ng:///home/directoryling/appname-play.component.html (173,41): The left-hand side of an arithmetic operation ...

Exploring the distinction between "() => void" and "() => {}" in programming

Exploring TS types, I defined the following: type type1 = () => {} type type2 = () => void Then, I created variables using these types: const customType1: type1 = () => { } const customType2: type2 = () => { } The issue surfaced as "Type ...

How to properly pass a parameter value to an Angular service when using inject.get in the constructor

After experimenting with injecting services in Angular, I encountered an issue when trying to call LogService within GlobalErrorHandler. Despite my best efforts, the code below just wouldn't work. I discovered that the problem stemmed from not passin ...

I'm having issues with the functionality of my code inside the ng-template and ngIf. What could be

I am facing an issue with my method that is supposed to display the specified HTML content. However, it seems like the ngIf condition within the code block is not functioning correctly. Can someone help me understand why the ngIf statement is being ignored ...

The properties of the extended Array class in TypeScript are not able to be accessed

It might be the late hour or my brain overloaded with programming, but I'm completely puzzled by this simple class: export class Path extends Array { constructor(...params:Array<any>) { super(...Object.assign([], arguments)); } ...

An issue arises in Slate.js when attempting to insert a new node within a specified region, triggering an error

A relevant code snippet: <Slate editor={editor} value={value} onChange={value => { setValue(value); const { selection } = editor; // if nothing is currently selected under the cursor if (select ...

Typescript - Inline check for undefined not properly functioning (Potential 'undefined' object detected.ts(2532))

I encountered an issue with TypeScript: const myFunction = ( param1: string | undefined, param2: { someProp: string } | undefined ) => { if (!param1 && !param2) { return; } // I am facing this Typescript error here: // (parame ...

Configuring the requestTimeout for an HTTP server in TypeScript

Recently, our team faced the task of upgrading our Node version to 18.4 for various reasons. An interesting update in Node 18 is that the http server now enforces a default 5 minute timeout for requests, which unfortunately disrupts the functionality of ce ...

Fixing the forwardRef issue with react-router-dom and material-ui

Despite implementing the forwardRef as recommended in various posts and Material-UI website examples, I am still encountering a warning in the console that has me puzzled. I am working on setting up a drawer with a list of items that are React Router link ...

merging pictures using angular 4

Is there a way in Angular to merge two images together, like combining images 1 and 2 to create image 3 as shown in this demonstration? View the demo image ...

Exploring the concept of converting a data type into an interface using map operations

Recently, I started learning Angular and I've been trying to wrap my head around how mapping from a response to an interface actually works. Let's take a look at the API response I received: [ { "id": 2, "name" : &qu ...

Checking for non-overlapping number ranges in an array in React/Javascript before submitting

I am faced with a challenge involving a list of values containing start and end address values. I need to ensure that when submitting these values, there are no existing ranges or overlaps within them. [ { "id": 23, "startAddress&quo ...

Error TS2559 occurs when the type '{ children: never[]; }' does not share any properties with the type 'IntrinsicAttributes & { post?: IPost | undefined; }'. This discrepancy in properties causes a type mismatch in the code

I'm having trouble understanding what the problem is. I have already looked at answers to this Error here, and I noticed that one of the issues others faced was a repetition of a function name and not giving props to a component in the component tree. ...

Do we need to import Vue in every component when using Nuxt with TypeScript?

I recently integrated TypeScript into Nuxt using the guidelines provided in the documentation: However, I have a specific question regarding component setup. Should I always include import vue from "vue" and export default Vue.extend ({}); in al ...