Home > Software design >  Typescript union type for functions
Typescript union type for functions

Time:08-11

I have a problem with understanding of Typescript type describing my validation functions. These are the types of functions that I use with my code:

type ValidationFunctionType = {
    fieldName: string;
    fields: string;
};

type MaxYearType = ValidationFunctionType & { fieldValue: Date; args: { maxYear: Date } };

type MinLengthType = ValidationFunctionType & {
    fieldValue: string;
    args: { minLength: number };
};

type MinNumberType = ValidationFunctionType & {
    fieldValue: number;
    args: { minNumber: number };
};

Then I create a union type for all the functions:

export type ValidationFunctionsType =
    | (({}: MinLengthType) => string)
    | (({}: MinNumberType) => string)
    | (({}: MaxYearType) => string);

These are the functions that I use with these function types:

const minLength = ({ fieldName, fieldValue, fields, args }: MinLengthType) => {
    return '';
};

const maxYear = ({ fieldName, fieldValue, fields, args }: MaxYearType) => {
    return '';
};

const minNumber = ({ fieldName, fieldValue, fields, args }: MinNumberType) => {
    return '';
};

And when I create an array with the above functions and use them in map:

const validationFunction: { func: ValidationFunctionsType}[] = [{
    func: minLength
  }, { func: maxYear}];

validationFunction.map(data => data.func({ fieldName: 'adfa', fieldValue: 'asdda', fields: 'sd', args: {
    minLength: 5
  } }));

I get the error message for field fieldName:

(property) fieldValue: never Type 'string' is not assignable to type 'never'.(2322) input.tsx(9, 2): The expected type comes from property 'fieldValue' which is declared here on type 'ValidationFunctionType & { fieldValue: string; args: { minLength: number; }; } & { fieldValue: number; args: { minNumber: number; }; } & { fieldValue: Date; args: { ...; }; }'

And for args field:

(property) args: { minLength: number; } & { minNumber: number; } & { maxYear: Date; } Type '{ minLength: number; }' is not assignable to type '{ minLength: number; } & { minNumber: number; } & { maxYear: Date; }'. Property 'minNumber' is missing in type '{ minLength: number; }' but required in type '{ minNumber: number; }'.(2322)

Why these types for field args and fieldName are connected with & operator instead of | operator? How to create a type for these functions which will be correct and more generic?

The whole code example in TS playground: Code

UPDATE

What I want to achieve in this example is to have a correctly designed type for all functions used for validation.

This type seems to work for Typescript:

type ValidationFunctionType = ({fieldName, fields, fieldValue, args}: MinLengthType | MinNumberType | MaxYearType) => string;

Working example But I'm not sure if it is the correct solution. Every function needs validation of fieldName and args type at the beginning.

CodePudding user response:

This seems to be a classic case of function overloading.

In your case, you should implement the signatures:

function validate(fieldName: string, fields: string, fieldValue: Date; args: { maxYear: Date }):string;

function validate(fieldName: string, fields: string, fieldValue: string; args: { maxYear: number }):string;

Finally, your implementation of validate should support every case - you can use type guard for type checking

Reference for function overloading: https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads

CodePudding user response:

data.func({ fieldName: 'adfa', fieldValue: 'asdda', fields: 'sd', args: {
  minLength: 5
}}

You're passing in values here that make sense if the function is expecting a MinLengthType. But since data.func is a ValidationFunctionsType, the type doesn't actually show that that's the expected data. The function might instead be expecting a MinNumberType or a MaxYearType, and the object you've passed would be an error for those, hence typescript shows an error.

Since there's 3 possible functions you might be dealing with, typescript will only allow you to pass in values that match all 3 functions. Ie, you can pass in the intersection of all 3 types: MinLengthType & MinNumberType & MaxYearType. Unfortunately, that intersection is impossible to obey because it requires mutually exclusive things like fieldValue being a string, a number, and a Date simultaneously. So there is no way to legally call data.func.

As for how to fix this... I'm not really sure what you're hoping to achieve from this code, so i may need more details. If you receive a function that expects, say, a MaxYearType, what do you want to do? Call the function with some alternate data; not call it at all; something else? Also, how do you intend to identify which function variant you have?

  • Related