#Conditional return type based on generic type's of parameter is wrong. What am I missing?

21 messages · Page 1 of 1 (latest)

granite arch
#

Hello,

I have the following code, and it says that the type is not assignable to T extends number ? HTMLElement : Text. What am I missing here?

function createElement<T extends number | string>(
    parameter: T
): T extends number ? HTMLElement : Text {
    if (typeof parameter === "string") {
        return document.createTextNode(parameter);
    }

    return new HTMLElement();
}
cobalt garden
#

conditionals don't really work in signatures, you might want overloads here, or just separate the functions

granite arch
#

Do you mean the implementation is supposed to be like this to make it conditional?

function createElement(parameter: string): Text;
function createElement(parameter: number): HTMLElement;

function createElement(parameter: number | string): HTMLElement | Text {
   ...implementation
}

Is this ideally supposed to be?

cobalt garden
#

that's how an overloaded function looks, yes

#

you don't really make it conditional

#

it's like combining function signatures

granite arch
#

I ended up implementing it:

export function createElement(parameter: string): Text;
export function createElement(parameter: number): HTMLElement;
export function createElement(parameter: string | number): Text | HTMLElement;
export function createElement<T extends number | string>(parameter: T) {
    ...implementation
}

Thank you

cobalt garden
#

fwiw if it's just a simple branch like in your initial example it might be better to just separate them like i mentioned before

granite arch
#

You mean something like this?

export function createElementA(parameter: string): Text {
     ...implementation
}
export function createElementB(parameter: number): HTMLElement {
    ...implementation
}

This is a simplified example to ask a question about conditional types for return. Do you see any cocern for creating different overloads for a function?

cobalt garden
#

overloads aren't checked, they're inherently unsafe, so if you can avoid them cleanly, in general you should do so

#

if it simplifies logic a lot, and you can't simplify it any other way, sure, but you gotta be careful implementing them

granite arch
#

What do you mean by they're inherently unsafe?

In my first example, it explictly checks the type of string. Isn't this supposed to be guaranteed to be safe?

    if (typeof parameter === "string") {
        parameter; // string
        return document.createTextNode(parameter);
    }

    parameter; // number
    return new HTMLElement();
#

Conditional return type based on generic type's of parameter is wrong. What am I missing?

wanton crystalBOT
#
function badImpl(x: string): number;
function badImpl(x: number): string;
function badImpl(x: string | number): string | number {
  // return true    // this would error, because it's definitely wrong.
  if (typeof x === "string") {
    return parseInt(x);
  }
  return x; // oops! no error, because ts just looks at the implementation signature.
}

const result = badImpl(0);
//    ^? - const result: string
//                       but is number at runtime
cobalt garden
#

ts doesn't fully guard you from making an incorrect implementation

granite arch
#

So it can flag errros at runtime... I was only thinking of the current implementation that returns the expected type.

cobalt garden
#

in a "sound" or a "strict" type system, types would always match the runtime values, or that branch just wouldn't get reached
but ts has some unsound features that make it more practical to use, such as assertions
since assertions in ts are a compile-time only, they can make your runtime values misalign with your types, leading to bugs (where strict languages would error at runtime instead)

#

overloads (along with some other features) fall into the same boat

granite arch
#

So is your suggestion to separate functions if the logic is simple?

cobalt garden
#

yeah

#

if it's just going to branch like in your initial example, may as well just branch before the function boundary