Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Argument of type 'string | undefined' is not assignable to parameter of type '"untitled" | null | undefined'

I can't understand something about TypeScript's type inference. I thought this should be valid and wouldn't require me to specify the type of T:

export const deflt = <T>(val: (T | undefined | null), defaultVal: T): T => {
  if (val === undefined || val === null) {
    return defaultVal;
  }
  return val;
}

function maybeLabelName(name: string | undefined) {
  return deflt(name, 'untitled');
}

The call to deflt fails with the following error:

Argument of type 'string | undefined' is not assignable to parameter of type '"untitled" | null | undefined'.
  Type 'string' is not assignable to type '"untitled" | null | undefined'. [2345]

I can fix it by changing the deflt call to one of the following:

// Option 1:
return deflt<string>(name, 'untitled');

// Option 2:
const s: string = 'untitled';
return deflt(name, s);

Shouldn't TypeScript infer the string type automatically?

like image 685
Nurpax Avatar asked Oct 17 '22 10:10

Nurpax


1 Answers

I think this is a bit of a quirk in the way typescript infers T in this case. Because you specify the constant 'untitled', the compiler will infer T as the string literal type 'untitled', since that is the simplest site to infer T from. It will then check the other parameter against the string literal type and find that string is not assignable to 'untitled'. A work around would be to decrease the priority of the second parameter when inferring T by adding an extra intersection with {}:

export const deflt = <T>(val: (T | undefined | null), defaultVal: T & {} ): T => {
    if (val === undefined || val === null) {
        return defaultVal;
    }
    return val;
}

function maybeLabelName(name: string | undefined) {
    return deflt(name, 'untitled'); // T is string now
}

Another solution (if ou can't change the original function) would be to explicitly assert untitled to string, which is basically what you already tried.

function maybeLabelName(name: string | undefined) {
    return deflt(name, 'untitled' as string); // T is string now
}
like image 90
Titian Cernicova-Dragomir Avatar answered Oct 21 '22 03:10

Titian Cernicova-Dragomir