1
votes

formatISODate function expects string value.

There are many strings which I need to convert if not undefined. I could do as follows:

{
  date1: date1 ? formatISODate(date1) : undefined,
  date2: date2 ? formatISODate(date2) : undefined
  ...
}

To avoid repeating the same ternary expression, I wrote the function below.

function convertDate<T extends string | undefined>(isoDate?: string): T {
  return isoDate ? formatISODate(isoDate) : undefined;
}

But it has a type error.

Error: 'undefined' is assignable to constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.

Is it possible to solve type problem for this use case?

1
does formatISODate have a type parameter? If not I'm not sure what the T buys you. This function should just return the return value of formatISODate | undefinedReid Evans
formatISODate does have type, namely it expects string and returns string. return string | undefined will show a type error when using date1: convertDate(date1), if date1 is stricly a string.kk-dev11
I misread earler. Simply type it as convertDate(isoDate?: string): string. That should work assuming you don't have strict null checks enabled in your tsconfig (which I'd doubt you do given your inline ternary seems to be working)Reid Evans

1 Answers

3
votes

Let's make the example even easier.

function convertDate<T extends string | undefined>(isoDate?: string): T {
   return undefined
}

'undefined' is assignable to the constraint of type 'T'

Means: What you return in the function (undefined), matches the constraints of your generic type parameter T (extends string | undefined).

, but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.

Means: TypeScript does not consider that as safe. What if you defined your function like this at compile time:

// expects string return type according to generics
// but you return undefined in function body
const res = convertDate<string>("2019-08-16T16:48:33Z")

Then according to your signature, you expect the return type to be string. But at runtime that is not the case! This discrepancy that T can be instantiated with a different subtype (here string) than you return in the function (undefined) is expressed with the TypeScript error.

Possible solutions:

The only way to convince the compiler is to hard cast the return type:

declare function formatISODate(d: string): string

function convertDate<T extends string | undefined>(isoDate?: string): T {
  return (isoDate ? formatISODate(isoDate) : undefined) as T
}

But I am not sure, if you really want that. The generic type parameter is pretty useless the way you define it in your function, as it not used in your parameters. Why not just write

function convertDate(isoDate?: string): string | undefined {
  return isoDate ? formatISODate(isoDate) : undefined;
}

If your goal is just to avoid the repeating of the same ternary expression, you could achieve that with above code.