''not assignable to type never' after using type predicate

I am trying to combine a technique for creating strongly typed strings with type predicates but failing, as the following bit of code shows: enter image description here

The error is TS2339: Property 'substr' does not exist on type 'never', but fixing this is exactly what the factory function should have ensured:

export function isShortDateTimeWithSpaceDelimiter(
  datetime: string | ShortDateTimeWithSpaceDelimiter
): datetime is ShortDateTimeWithSpaceDelimiter {
  return DateTime.fromFormat(datetime, FORMAT_ISO_DATE_SPACE_DATE_TIME).isValid;
}

export function toShortDateTimeWithSpaceDelimiter(
  datetime: string
): ShortDateTimeWithSpaceDelimiter {
  if (isShortDateTimeWithSpaceDelimiter(datetime)) return datetime;
  throw new TypeError("Not a ShortDateTimeWithSpaceDelimiter");
}

Since TS assumes the return type from the factory is never, I guess this is not working as I had intended ... What am I missing?

The type definitions look like this:

// https://spin.atomicobject.com/2017/06/19/strongly-typed-date-string-typescript/
enum LocalDateTimeWithSpaceDelimiterBrand {}

/** Minute granularity: "yyyy-MM-dd hh:mm" */
export type ShortDateTimeWithSpaceDelimiter = string & LocalDateTimeWithSpaceDelimiterBrand;


Solution 1:[1]

It provides little to no utility because it's literally never. I think using a typical intersection with a branding property would work better here to serve as nominal typing. (string & { __ShortDateTimeWithSpaceDelimiter__: unknown }). But maybe even then this is does not need nominal typing? It is much overhead for little gain anyways ?

The type ShortDateTimeWithSpaceDelimiter is really just an alias for never, so it's practically useless and isn't as helpful as it should be when defining a different "type" of string.

There is also a playground example of nominal typing: playground

So here you could use string & { __brand: "ShortDateTimeWithSpaceDelimiter" }.

However I do suggest throwing all of this away altogether and stick with the simple string. Why is all of this necessary? Overkill occurs when types start being defined by code and act like code! Types should influence the design and implementation, not the other way around.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 hittingonme