Ensuring the type safety of arguments in TypeScript through conditional checking based on the previous argument

I am looking to implement type-checking for function arguments, where the properties of the second argument are based on the properties of the previous one.

The config variable should only contain properties present in objects within the values array. These properties should be optional (not all required in config, but cannot add additional ones).

Here is an example code snippet:

type CustomType <T> = {
  [K in keyof T]: number
};
type Config <T> = {
  [K in keyof T]? : {
    highPriority: boolean;
    callback: (values: any[]) => number[];
  }
};

const customFunction = <T>(values: T[], config: Config <T> ): Array <CustomType<T>> => {
  // logic...

  return [];
};

const values = [
  {
    foo: 'foo',
    bar: 'bar'
  },
  {
    foo: 'foo',
    bar: 'bar'
  }
];

// Should optionally contain only "foo" and "bar" properties in this example
const config = {
  foo: {
    highPriority: true,
    callback: () => []
  },
  // not present in values objects
  wrong: {
    highPriority: true,
    callback: () => []
  }
};

// Error should be displayed for 'config' as "wrong" property is not present in 'values' objects
const result = customFunction(values, config);

In the final line, config must show an error because it introduces a "wrong" property that isn't present in the original values object.


I can enforce some checking by implementing an interface for the config, but I believe there's a way to do it without this step.

interface ISpecific {
  foo: any,
  bar: any
}

const values: ISpecific[] = [
  {
    foo: 'foo',
    bar: 'bar'
  },
  {
    foo: 'foo',
    bar: 'bar'
  }
];

const config: Config<ISpecific> = {
  // ...
  // The wrong property is marked as an error
}

UPDATED:

  • config has been defined elsewhere and is not aware of the values variable
  • customFunction is used in various places throughout the application, so passing the config as an object literal is not feasible.

Any suggestions or assistance?

Answer №1

Typescript's behavior is to only check for excess properties when assigning an object literal directly to a variable or parameter of a specific type, as detailed here. To address this in your scenario, you have the option to explicitly type the config variable or use an object literal directly:

const values = [
    {
        foo: 'foo',
        bar: 'bar'
    },
    {
        foo: 'foo',
        bar: 'bar'
    }
];

// We can define config based on the values without needing an additional interface
const config: Config<typeof values[number]> = {
    foo: {
        highPriority: true,
        callback: () => []
    },
    // This will cause an error
    wrong: {
        highPriority: true,
        callback: () => []
    }
};

// Alternatively, pass the object literal directly 
const result2 = customFunction(values, {
    foo: {
        highPriority: true,
        callback: () => []
    },
    // This will also trigger an error
    wrong: {
        highPriority: true,
        callback: () => []
    }
});

Another approach involves using conditional types and an additional parameter to flag an error if the passed type contains extra properties:

type NoExtraProperties<TSource, TTarget> = Exclude<keyof TSource, keyof TTarget> extends never ? true : "Extra properties detected";
const customFunction = <T, TConfig extends Config<T>>(values: T[], config: TConfig, validate: NoExtraProperties<TConfig, T>): Array<CustomType<T>> => {
    // implementation...

    return [];
};

// The following line will throw an error
const result = customFunction(values, config, true);

For further experimentation, you can visit the 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

Unraveling Props in a React Component Using Typescript and a Union Interface

In my Props Interface, I have split it into a base interface and two Union types: interface BaseProps { ... } interface ControlledInput extends BaseProps { value: string; onChange: ...; } interface UncontrolledInput extends BaseProps { defaultVa ...

How can variables from state be imported into a TypeScript file?

Utilizing vue.js along with vuetify, I have a boolean value stored in state via Vuex defined in src/store/index.ts (named darkMode). This value is used within one of my view components inside a .vue file. However, I now wish to access the same variable in ...

Tips on how to properly handle Promises in a constructor function

My Angular Service is currently making http requests, but I am looking to retrieve headers for these requests from a Promise. The current setup involves converting the promise to an Observable: export class SomeService { constructor(private http: HttpCl ...

Is there a way to access the value of a public variable within the @input decorator of a function type?

I am working on a dropdown component that utilizes the @Input decorator to define a function with arguments, returning a boolean value. dropdown-abstract.component.ts @Input() public itemDisabled: (itemArgs: { dataItem: any; index: number }) => boo ...

Changing array in TypeScript/Angular

I'm attempting to transform an array of strings into an array of key-value pairs, like this: ["x", "y"] transforms into [{"Value":"x"}, {"Value":"y"}] Any tips or guidance would be highly appreciated. Thank you! ...

TS: Utilizing a generic parameter in an overloaded function call

This piece of code encapsulates the essence of what I'm trying to achieve more effectively than words: function A(a: string): string; function A(a: number): number; function A(a: any) { return a; } function B<T extends number | string>(arg: T): ...

A step-by-step guide on reading/loading a JSON file using Typescript

I'm fairly new to Typescript and I'm attempting to parse a simple JSON file using Typescript. After searching online and testing different solutions, I still haven't been able to find a straightforward code snippet that reads a local JSON fi ...

Type inference in Typescript is especially powerful when used in conjunction with decorators

I am curious as to why the compiler in Typescript cannot infer the new type of a class when decorators or annotations are used. Interestingly, if I use the traditional ES5 method (manually calling the decorator), it works without any issues. Here is an ex ...

Issue with Nativescript + Angular project: A critical error has occurred - Module not found: '@schematics/angular/utility/parse-name'

I'm diving into nativescript development for the first time and I'm attempting to create a new component using this command: ng g c component-name The error message that's popping up reads: An unhandled exception occurred - Cannot find modu ...

Typescript: organizing nested types within an interface

My goal is to create an interface CountersData based on my JSON data. The challenge lies in the nested id property, which contains an array of nested dictionaries. I want this property to be optional. However, I have not been successful in making it option ...

The JokesService (?) has encountered dependency resolution issues that Nest is unable to resolve

Currently delving into the world of NestJS and feeling a bit perplexed about the workings of "modules". In my project, I have two modules namely JokesModule and ChuckNorrisApiModule. My goal is to utilize the service provided by ChukNorrisService within th ...

Conditional types allow the function parameter type to be determined based on the type of another parameter

Here: function customFunction<T extends boolean> (param1: T, param2: T extends true ? number[] : number) { if (param1) { let result: number[] = param2; // Type 'number' is not assignable to type 'number[]'.(2322) } } ...

implementing an event listener in vanilla JavaScript with TypeScript

Can anyone help me figure out how to correctly type my event listener in TypeScript + Vanilla JS so that it has access to target.value? I tried using MouseEvent and HTMLButtonElement, but I haven't been successful. const Database = { createDataKeys ...

Jasmine: utilizing unit test to spy on the invocation of a nested function

When running unit tests for an Angular app, I need to spy on a function called func1 to check if it is being called. However, within func1 there is a call to func2 and I also want to spy on that to see if it is being called. How should I structure my unit ...

What is the process for incorporating a restriction in a map within a generic framework?

Note: The explanation provided was not clear enough; you might find it helpful to move on to the next example. I'm facing an issue with the constraints on generics and struggling to identify my mistake. Here is the code snippet: Code snippet goe ...

Angular8 Material Grid displaying headers and tiles, experiencing slight resizing issue with mat-grid-tile content

Exploring Angular8 Material: Grid Layout with Headers and Tiles Currently, I am delving into the workings of the grid system within Angular Material. I am dynamically fetching components and organizing them within a grid. While the grid list and tiles are ...

TypeScript: defining a custom type with a collection of properties following a consistent pattern

I am looking for a way to create a type that can accommodate any number of properties following a predefined pattern, like so: type Values = { id: number; id1?: number; id2?: number; id3?: number; // ... somethingElse: string; anotherOne: num ...

Challenge with Ahead-of-Time Compilation in Angular Dynamic Components

As part of the business logic requirements, it is necessary for me to extract the meta data from dynamic components (EntryComponents). The approach I am taking to extract this meta data is as follows: Retrieve all components within a module using Compon ...

The Typescript loop appears to be stuck and not moving through the

I have encountered a problem while trying to iterate through my array using foreach and forloop in an angular 8 application. Despite having 250 objects in the array, it is not iterating through any elements. I am unable to figure out what the issue could b ...

Having trouble getting the npm package with @emotion/react and vite to function properly

Encountering an issue with the npm package dependencies after publishing, specifically with @emotion/react. This problem arose while using vite for packaging. Upon installing the package in another project, the css property appears as css="[object Ob ...