Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript Conditional Type complains Type not assignable

Tags:

typescript

I am trying to understand how TypeScript conditional type works. Here is my code. There are type errors:

interface MyType {
  name: string;
}

const testFunc = <T extends MyType | string>(
  what: T
): T extends MyType ? MyType : string => {
  if (typeof what === 'object') {
    return what['name'];
  }
  return what;
};

What is the correct usage?

enter image description here

enter image description here

like image 351
techguy2000 Avatar asked Apr 11 '19 21:04

techguy2000


People also ask

How do you add a condition in TypeScript?

Syntax: We can create the conditional types with the used of ternary operator and extends in TypeScript. Type1 extends Type2 ? for One value : for different value; Here extend works as compare function which checks is Type1 have properties of Type2 if yes then it jumps to true branch else in the false branch.

How do you check TypeScript type?

Use the typeof operator to check the type of a variable in TypeScript, e.g. if (typeof myVar === 'string') {} . The typeof operator returns a string that indicates the type of the value and can be used as a type guard in TypeScript.

What is infer keyword in TypeScript?

The infer keyword allows types to be extracted from conditions in conditional types. This is often used within Typescript's standard utility types. In the next lesson, we will gain a deep understanding of some of TypeScripts standard conditional utility types.

What are conditional types How do you create them?

Conditional types help describe the relation between the types of inputs and outputs. When the type on the left of the extends is assignable to the one on the right, then you'll get the type in the first branch (the “true” branch); otherwise you'll get the type in the latter branch (the “false” branch).


1 Answers

The function TestFunc in your code is supposed to return string in every case. I think it is a kind of typo. Let's fix it and go on.

Later I came up with a more safe solution (I leave my old answer at the bottom). It is better to use overloading. In an overloading you describe the conditional logic and in the function you use union types.

interface MyType {
  name: string;
}

function testFunc<T extends MyType | string>(
  what: T
): T extends MyType ? string : MyType;

function testFunc(what: MyType | string): MyType | string {
  if (typeof what === 'object') {
    return what.name;
  }
  return { name: what };
}

The old answer:

interface MyType {
  name: string;
}

type TestFunc = <T extends MyType | string>(what: T) => T extends MyType ? string : MyType;

const testFunc: TestFunc = (what: any) => {
  if (typeof what === 'object') {
    return what.name;
  }
  return { name: what };
};

Or if you prefer to define the type inline:

interface MyType {
  name: string;
}

const testFunc: <T extends MyType | string>(what: T) =>
  T extends MyType ? string : MyType =
  (what: any) => {
    if (typeof what === 'object') {
      return what.name;
    }
    return { name: what };
  };

Typescript compiler will handle it like this:

const a1: MyType = testFunc({ name: 'foo' }); // Type 'string' is not assignable to type 'MyType'.

const a2: MyType = testFunc({ name: 1 }); // Error: Argument of type '{ name: number; }'
//                                is not assignable to parameter of type 'string | MyType'

const a3: string = testFunc({ name: 'foo' }); // Ok

const a4: string = testFunc('foo'); // Error: Type 'MyType' is not assignable to type 'string'.

const a5: MyType = testFunc('foo'); // Ok
like image 158
Andrei Kovalev Avatar answered Sep 20 '22 01:09

Andrei Kovalev