Home > Enterprise >  Typescript - Computed generic type not working well
Typescript - Computed generic type not working well

Time:12-01

I'm trying to set a Generic type that will accept 2 parameters and return a function.
First parameter - The type of the single parameter of the returned function
Second Parameter - need to get true if dev wants the returned function parameter to be required.

Somehow it's just not working The Val is inferred to string but it still thinks it's not a string

Any help will be appreciated

Link to playground

// Mandatory = true for required parameters
export type ValidationFunction<T = unknown, IsMandatory = unknown> =
 <Val = IsMandatory extends true ? T : T | undefined>(val: Val) => true | string;


const test: ValidationFunction<string, true> = (val) => { // error!
//    ~~~~
//  Type 'Val' is not assignable to type 'string | true'.
  return val;
};

test('poop')

// Maybe the core of the issue but weirdly it accepts
// any type of parameter I'll pass to it

test(555)
test(true)
test(null)
test({})

CodePudding user response:

Invalid answer:

Looks to me like you're saying that your ValidationFunction for some reason wants to always return string | true - why?

I'd just do it like that

export type ValidationFunction<T = unknown, IsMandatory = unknown> =
 <Val = IsMandatory extends true ? T : T | undefined>(val: Val) => T;
test('poop') // no warnings


test(555) // warning 
test(true) // warning
test(null) // warning
test({}) // warning
test([]) // warning

Valid answer:

// Mandatory = true for required parameters
export type ValidationFunction<T = unknown, IsMandatory = unknown> =
  (val: IsMandatory extends true ? T : T | undefined) => true | string;


const test: ValidationFunction<string, true> = (val) => {
  return val;
};

test('poop')

// Maybe the core of the issue but weirdly it accepts any type of parameter I'll pass to it

test(555)
test(true)
test(null)
test({})

Answer by @jcalz in the comment of the OP's question.

CodePudding user response:

The problem is that you are making ValidationFunction<T, M> itself a generic function with one type parameter Val which has a default of IsMandatory extends true ? T : T | undefined. But it doesn't look like you intend for it to be generic at all, and even if you did, the type parameter Val merely defaults to something; it isn't constrained at all. So test accepts and returns a completely unconstrained Val, which may or may not have anything to do with string.

If you change the default to a constraint, things start working how you want:

export type ValidationFunction<T = unknown, IsMandatory = unknown> =
  <Val extends IsMandatory extends true ? T : T | undefined>(val: Val) => true | string;
//     ^^^^^^^ <-- constraint, not default

const test: ValidationFunction<string, true> = (val) => {
  return val;
}; // okay

test('           
  • Related